文章目录
把之前那篇java多线程重新排版一下。
1. 进程和线程
单核计算机中,CPU在某一时间点只能做一件事,多进程不能同时进行,而是高速交替运行。也就是说不能并发。
多进程不能提高速度,而是提高cpu使用率。
进程与进程之间内存独立。
多线程也不能提高速度,而是提高应用程序使用率。比如,多个客户端访问一个服务器。
线程和线程共享堆内存和方法区内存,栈内存独立。
java命令启动JVM等于启动一个进程,该进程自动启动一个主线程来调用main。在此之前所有线程都是单线程的。
1.1 线程的生命周期
5个状态:新建,就绪,运行,阻塞,消亡。
1.2 线程创建
定义线程类
public class ThreadTest01{
public static void main(String[] args){
Thread t = new Processor();
t.start();
for(int i=0;i<10;i++){
System.out.println("main-->"+i);
}
}
}
class Processor extends Thread{
public void run(){
for(int i=0;i<10;i++){
System.out.println("run-->"+i);
}
}
}
/*
main-->0
main-->1
main-->2
main-->3
main-->4
main-->5
main-->6
main-->7
main-->8
main-->9
run-->0
run-->1
run-->2
run-->3
run-->4
run-->5
run-->6
run-->7
run-->8
run-->9
*/
实现Runnable接口
实现java.lang.Runnable
,实现run()
。
推荐这种方法,因为保留了类的继承。
public class ThreadTest02{
public static void main(String[] args){
Thread t = new Thread(new Processor());
t.start();
for(int i=0;i<10;i++){
System.out.println("main-->"+i);
}
}
}
class Processor implements Runnable{
public void run(){
for(int i=0;i<10;i++){
System.out.println("run-->"+i);
}
}
}
/*
main-->0
run-->0
main-->1
main-->2
main-->3
main-->4
main-->5
main-->6
run-->1
run-->2
run-->3
run-->4
run-->5
run-->6
run-->7
main-->7
main-->8
main-->9
run-->8
run-->9
*/
public class ThreadTest03{
public static void main(String[] args){
Thread t1 = new Thread(new Processor());
t1.start();
Thread t2 = Thread.currentThread();
System.out.println(t2.getName());
System.out.println(t2);
Thread t3 = new Thread(new Processor());
t3.setName("t3");
t3.start();
}
}
class Processor implements Runnable{
public void run(){
Thread t = Thread.currentThread();
System.out.println(t.getName());
System.out.println(t);
}
}
/*
main
Thread-0
Thread[Thread-0,5,main]
Thread[main,5,main]
t3
Thread[t3,5,main]
*/
2. 优先级
public final void setPriority(int newPriority)
public class ThreadTest04{
public static void main(String[] args){
Thread t1 = new Thread(new Processor());
System.out.println(t1.getPriority());
t1.setPriority(Thread.MAX_PRIORITY);
System.out.println(t1.getPriority());
Thread t2 = new Thread(new Processor());
System.out.println(t2.getPriority());
t2.setPriority(Thread.MIN_PRIORITY);
System.out.println(t2.getPriority());
t1.start();
t2.start();
}
}
class Processor implements Runnable{
public void run(){
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
}
/*
5
10
5
1
Thread-0--->0
Thread-0--->1
Thread-0--->2
Thread-0--->3
Thread-0--->4
Thread-0--->5
Thread-0--->6
Thread-0--->7
Thread-0--->8
Thread-0--->9
Thread-1--->0
Thread-1--->1
Thread-1--->2
Thread-1--->3
Thread-1--->4
Thread-1--->5
Thread-1--->6
Thread-1--->7
Thread-1--->8
Thread-1--->9
*/
3. sleep()
public static void sleep(long millis) throws InterruptedException
该方法为其它线程让出时间片,进入阻塞状态,睡够了就进入就绪状态。睡眠中若被中断,会抛出InterruptedException
interrupt()
依靠异常处理机制可以打断睡眠。
下面的程序5秒后有输出。
public class SleepTest01{
public static void main(String[] args){
Thread t1 = new Thread(new Processor());
t1.start();
try{
Thread.sleep(5000);
}catch(InterruptedException e){
e.printStackTrace();
}
//依靠异常处理机制
t1.interrupt();
}
}
class Processor implements Runnable{
public void run(){
try{
Thread.sleep(10000000000L);
}catch(InterruptedException e){
e.printStackTrace();
}
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName());
}
}
}
public class SleepTest02{
public static void main(String[] args){
//Thread t1 = new Thread(new Processor());
Processor p = new Processor();
Thread t1 = new Thread(p);
t1.start();
try{
Thread.sleep(5000);
}catch(InterruptedException e){
e.printStackTrace();
}
//t1.run = false;
p.run = false;
}
}
class Processor implements Runnable{
boolean run = true;
public void run(){
for(int i=0;i<10;i++){
if(run){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--->"+i);
}else{
return ;
}
}
}
}
java.lang.Object
的其它相关方法:
wait()
,线程等待notify()
,唤醒
3.1 yield()
public static void yield()
让位方法与睡眠类似,只是时间不固定,只能把时间片让给同优先级的线程。
public class YieldTest{
public static void main(String[] args){
Thread t1 = new Thread(new Processor());
t1.start();
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
}
class Processor implements Runnable{
public void run(){
for(int i=0;i<100;i++){
System.out.println(Thread.currentThread().getName()+"--->"+i);
if(i%20==0){
Thread.yield();
}
}
}
}
3.2 交替输出
notifyAll()
:Wakes up all threads that are waiting on this object’s monitor.
管程 (英语:Monitors,也称为监视器) 是操作系统中一种重要的程序结构,结构内的多个子程序(对象或模块)形成的多个工作线程互斥访问共享资源。这些共享资源一般是硬件设备或一群变量。管程实现了在一个时间点,最多只有一个线程在执行管程的某个子程序。与那些通过修改数据结构实现互斥访问的并发程序设计相比,管程实现很大程度上简化了程序设计。 管程提供了一种机制,线程可以临时放弃互斥访问,等待某些条件得到满足后,重新获得执行权恢复它的互斥访问。
public class T{
public static void main(String[] args){
Num num = new Num(1);
Thread t1 = new Thread(new PrintOdd(num));
Thread t2 = new Thread(new PrintEven(num));
t1.setName("t1");
t2.setName("t2");
t1.start();
try{
Thread.sleep(1000);
}catch(Exception e){
e.printStackTrace();
}
t2.start();
}
}
class Num{
int count;
Num(int count){
this.count = count;
}
public synchronized void printOdd(){
System.out.println(Thread.currentThread().getName()+"--->"+(count++));
this.notifyAll();
try{
Thread.sleep(1000);
this.wait();
}catch(Exception e){
e.printStackTrace();
}
}
public synchronized void printEven(){
System.out.println(Thread.currentThread().getName()+"--->"+(count++));
this.notifyAll();
try{
Thread.sleep(1000);
this.wait();
}catch(Exception e){
e.printStackTrace();
}
}
}
class PrintOdd implements Runnable{
Num num;
PrintOdd(Num num){
this.num = num;
}
public void run(){
while(true){
num.printOdd();
}
}
}
class PrintEven implements Runnable{
Num num;
PrintEven(Num num){
this.num = num;
}
public void run(){
while(true){
num.printEven();
}
}
}
wait()、notify/notifyAll()
在获得锁的synchronized
代码块中执行;当线程执行wait()
方法时候,会释放当前的锁,然后让出CPU,进入等待状态。只有当notify()
或notifyAll()
被执行,才会唤醒一个或多个正处于等待状态的线程,然后继续往下执行,直到执行完synchronized
代码块的代码或是中途遇到wait()
,再次释放锁。
4. join()
public final void join() throws InterruptedException
成员方法,两个栈变成一个栈,执行有先后顺序。
把下面代码注释去掉,前后对比一下。
public class JoinTest{
public static void main(String[] args){
Thread t1 = new Thread(new Processor());
t1.start();
// try{
// t1.join();
// }catch(InterruptedException e){
// e.printStackTrace();
// }
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
}
class Processor implements Runnable{
public void run(){
for(int i=0;i<10;i++){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
}
5. 锁
5.1 同步异步
异步编程模型:线程之间互不等待。
同步编程模型:线程1等待线程2结束后才能执行。降低程序使用率,但保证数据安全。此时等同单线程。
什么时候用同步:
- 多线程
- 多线程共享数据
- 共享数据有修改操作
可以通过sleep()
来验证异步模型的不安全性。
public class LockTest01{
public static void main(String[] args){
Account act = new Account("account0001",2000);
Thread t1 = new Thread(new Processor(act));
Thread t2 = new Thread(new Processor(act));
t1.start();
t2.start();
}
}
class Account{
private String actNo;
private double balance;
public Account(){}
public Account(String actNo,double balance){
this.actNo = actNo;
this.balance = balance;
}
public void setActno(String actNo){
this.actNo = actNo;
}
public void setBalance(double balance){
this.balance = balance;
}
public String getActno(){
return actNo;
}
public double getBalance(){
return balance;
}
public void withdraw(double money){
double after = balance - money;
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
this.setBalance(after);
}
}
class Processor implements Runnable{
Account act;
Processor(Account act){
this.act = act;
}
public void run(){
act.withdraw(1000.0);
System.out.println("withdraw 1000.0,balance: "+act.getBalance());
}
}
/*
withdraw 1000.0,balance: 1000.0
withdraw 1000.0,balance: 1000.0
*/
5.2 两种同步方法
同步对象
这种方法更精确,推荐。
synchronized(共享对象){
同步代码
}
线程遇到synchronized会找共享对象的对象锁,找到后进入同步语句块执行,执行结束后归还锁。没找到则等待其它线程归还锁。
public class LockTest02{
public static void main(String[] args){
Account act = new Account("account0001",2000);
Thread t1 = new Thread(new Processor(act));
Thread t2 = new Thread(new Processor(act));
t1.start();
t2.start();
}
}
class Account{
private String actNo;
private double balance;
public Account(){}
public Account(String actNo,double balance){
this.actNo = actNo;
this.balance = balance;
}
public void setActno(String actNo){
this.actNo = actNo;
}
public void setBalance(double balance){
this.balance = balance;
}
public String getActno(){
return actNo;
}
public double getBalance(){
return balance;
}
public void withdraw(double money){
synchronized(this){
double after = balance - money;
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
this.setBalance(after);
}
}
}
class Processor implements Runnable{
Account act;
Processor(Account act){
this.act = act;
}
public void run(){
act.withdraw(1000.0);
System.out.println("withdraw 1000.0,balance: "+act.getBalance());
}
}
/*
withdraw 1000.0,balance: 1000.0
withdraw 1000.0,balance: 0.0
*/
同步方法
public synchronized void withdraw(){}
这种方法,线程仍然拿走共享对象的锁。
java.lang.StringBuffer
等模块的很多方法都有synchronized
,是线程安全的。
方法没有synchronized
声明则不用等待锁,下面的代码,m2()
不需要等待m1()
释放锁。
public class LockTest03{
public static void main(String[] args){
MyClass mc = new MyClass();
Thread t1 = new Thread(new Processor(mc));
t1.setName("t1");
Thread t2 = new Thread(new Processor(mc));
t2.setName("t2");
t1.start();
//t1 execute first
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
t2.start();
}
}
class MyClass{
public synchronized void m1(){
try{
Thread.sleep(5000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("m1...");
}
//needn't wait for m1 to finish
public void m2(){
System.out.println("m2...");
}
}
class Processor implements Runnable{
MyClass mc;
public Processor(MyClass mc){
this.mc = mc;
}
public void run(){
String name = Thread.currentThread().getName();
if("t1".equals(name)){
mc.m1();
}else if("t2".equals(name)){
mc.m2();
}
}
}
5.3 类锁
类只有一个,所以类锁是类级别的。
synchronized
添加到静态方法上,线程执行到此会找类锁。即使不共享对象,同步静态方法也要等待其它线程释放锁。
public class LockTest04{
public static void main(String[] args){
MyClass mc1 = new MyClass();
MyClass mc2 = new MyClass();
Thread t1 = new Thread(new Processor(mc1));
t1.setName("t1");
Thread t2 = new Thread(new Processor(mc2));
t2.setName("t2");
t1.start();
//t1 execute first
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
t2.start();
}
}
class MyClass{
public synchronized static void m1(){
try{
Thread.sleep(5000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("m1...");
}
//looking for the lock of class
public synchronized static void m2(){
System.out.println("m2...");
}
}
class Processor implements Runnable{
MyClass mc;
public Processor(MyClass mc){
this.mc = mc;
}
public void run(){
String name = Thread.currentThread().getName();
if("t1".equals(name)){
mc.m1();
}else if("t2".equals(name)){
mc.m2();
}
}
}
5.4 死锁
synchronized
里面嵌套synchronized
时容易发生。
public class DeadLock{
public static void main(String[] args){
Object o1 = new Object();
Object o2 = new Object();
Thread t1 = new Thread(new T1(o1,o2));
Thread t2 = new Thread(new T2(o1,o2));
t1.start();
t2.start();
}
}
class T1 implements Runnable{
Object o1;
Object o2;
public T1(Object o1,Object o2){
this.o1 = o1;
this.o2 = o2;
}
//lock o1,then lock o2
public void run(){
synchronized(o1){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
synchronized(o2){
}
}
}
}
class T2 implements Runnable{
Object o1;
Object o2;
public T2(Object o1,Object o2){
this.o1 = o1;
this.o2 = o2;
}
//lock o2,then lock o1
public void run(){
synchronized(o2){
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
synchronized(o1){
}
}
}
}
6. 守护线程
线程分两种:
- 用户线程
- 守护线程
所有用户线程都结束时,守护线程才会结束,比如GC。
所以java命令启动了jvm、一个主线程,和一个GC线程。
public final void setDaemon(boolean on)
public class DaemonTest{
public static void main(String[] args){
Thread t = new Thread(new T());
t.setDaemon(true);
t.start();
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+"-->"+i);
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
class T implements Runnable{
public void run(){
int i = 0;
while(true){
System.out.println(Thread.currentThread().getName()+"-->"+i);
i++;
try{
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
这个程序,如果true
改为false
,主线程结束后,线程 t 仍然继续输出。
7. 定时器
java.util.Timer
public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
java.util.TimerTask--public abstract void run()
import java.util.*;
import java.text.*;
import java.text.ParseException;
public class TimerTest{
public static void main(String[] args){
Timer t = new Timer();
try{
t.schedule(
new LogTimeTask(),
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2018-10-20 10:59:00"),
5*1000);
}catch(ParseException e){
e.printStackTrace();
}
System.out.println(1);
}
}
class LogTimeTask extends TimerTask{
public void run(){
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
}
}