在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口;Thread类是在java.lang包中定义的。一个类只要继承了Thread类同时覆写了本类中的run()方法就可以实现多线程操作了,但是一个类只能继承一个父类,这是此方法的局限。
1 public class Actor extends Thread { 2 public void run(){ 3 //线程执行的操作 4 } 5 }
在实际开发中一个多线程的操作很少使用Thread类,而是通过Runnable接口完成。
1 public class Actress implements Runnable{ 2 3 @Override 4 public void run() { 5 //线程执行的操作 6 } 7 }
在主方法中调用这两种线程。
1 public static void main(String[] args) { 2 Thread actor=new Actor(); 3 actor.setName("Mr.thread"); //Thread 线程 4 actor.start(); 5 6 Thread actressThread=new Thread(new Actress(),"Miss.Runnable"); //Runnable 线程 7 actressThread.start(); 8 }
两种实现方式的区别和联系:
在程序开发中只要是多线程肯定永远以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类有如下好处:
1、避免点继承的局限,一个类可以继承多个接口。
2、适合于资源的共享
以卖票程序为例,通过Thread类完成:
1 package multithreading; 2 3 public class MyThreadWithExtends extends Thread { 4 5 private int tickets = 10; 6 7 @Override 8 public void run() { 9 10 for (int i = 0; i <= 100; i++) { 11 if(tickets>0){ 12 System.out.println(Thread.currentThread().getName()+"--卖出票:" + tickets--); 13 } 14 } 15 } 16 17 18 public static void main(String[] args) { 19 MyThreadWithExtends thread1 = new MyThreadWithExtends(); 20 MyThreadWithExtends thread2 = new MyThreadWithExtends(); 21 MyThreadWithExtends thread3 = new MyThreadWithExtends(); 22 23 thread1.start(); 24 thread2.start(); 25 thread3.start(); 26 27 //每个线程都独立,不共享资源,每个线程都卖出了10张票,总共卖出了30张。如果真卖票,就有问题了。 28 } 29 30 }
运行结果:
Thread-0--卖出票:10
Thread-2--卖出票:10
Thread-1--卖出票:10
Thread-2--卖出票:9
Thread-0--卖出票:9
Thread-2--卖出票:8
Thread-1--卖出票:9
Thread-2--卖出票:7
Thread-0--卖出票:8
Thread-2--卖出票:6
Thread-2--卖出票:5
Thread-2--卖出票:4
Thread-1--卖出票:8
Thread-2--卖出票:3
Thread-0--卖出票:7
Thread-2--卖出票:2
Thread-2--卖出票:1
Thread-1--卖出票:7
Thread-0--卖出票:6
Thread-1--卖出票:6
Thread-0--卖出票:5
Thread-0--卖出票:4
Thread-1--卖出票:5
Thread-0--卖出票:3
Thread-1--卖出票:4
Thread-1--卖出票:3
Thread-1--卖出票:2
Thread-0--卖出票:2
Thread-1--卖出票:1
Thread-0--卖出票:1
如果用Runnable就可以实现资源共享,下面看例子:
1 package multithreading; 2 3 public class MyThreadWithImplements implements Runnable { 4 5 private int tickets = 10; 6 7 @Override 8 public void run() { 9 10 for (int i = 0; i <= 100; i++) { 11 if(tickets>0){ 12 System.out.println(Thread.currentThread().getName()+"--卖出票:" + tickets--); 13 } 14 } 15 } 16 17 18 public static void main(String[] args) { 19 MyThreadWithImplements myRunnable = new MyThreadWithImplements(); 20 Thread thread1 = new Thread(myRunnable, "窗口一"); 21 Thread thread2 = new Thread(myRunnable, "窗口二"); 22 Thread thread3 = new Thread(myRunnable, "窗口三"); 23 24 thread1.start(); 25 thread2.start(); 26 thread3.start(); 27 } 28 29 }
运行结果:
窗口二--卖出票:10
窗口三--卖出票:9
窗口一--卖出票:8
窗口三--卖出票:6
窗口三--卖出票:4
窗口三--卖出票:3
窗口三--卖出票:2
窗口三--卖出票:1
窗口二--卖出票:7
窗口一--卖出票:5
每个线程共享了对象myRunnable的资源,卖出的总票数是对的,但是顺序是乱的,怎么办?
同步关键词synchronized
线程执行的时候,一个个执行不就有序了。即线程1在执行的时候,其他线程阻塞不要执行。
加synchronize。
1 package multithreading.sync; 2 3 public class MyThreadWithImplements implements Runnable { 4 5 private int tickets = 10; 6 7 @Override 8 public synchronized void run() { 9 //同步关键词synchronized 10 for (int i = 0; i <= 100; i++) { 11 if(tickets>0){ 12 System.out.println(Thread.currentThread().getName()+"--卖出票:" + tickets--); 13 } 14 } 15 } 16 17 18 public static void main(String[] args) { 19 20 MyThreadWithImplements myRunnable = new MyThreadWithImplements(); 21 Thread thread1 = new Thread(myRunnable, "窗口一"); 22 Thread thread2 = new Thread(myRunnable, "窗口二"); 23 Thread thread3 = new Thread(myRunnable, "窗口三"); 24 25 thread1.start(); 26 thread2.start(); 27 thread3.start(); 28 } 29 30 }
运行结果
窗口一--卖出票:10
窗口一--卖出票:9
窗口一--卖出票:8
窗口一--卖出票:7
窗口一--卖出票:6
窗口一--卖出票:5
窗口一--卖出票:4
窗口一--卖出票:3
窗口一--卖出票:2
窗口一--卖出票:1
缺陷
1、如果这个获取锁的线程由于要等待IO或者其他原因(比如调用sleep方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,这多么影响程序执行效率。
2、当有多个线程读写文件时,读写操作会发生冲突现象,写操作会发生冲突现象,但是读操作不会发生冲突现象。但是采用synchronized关键字来实现同步的话,就会导致一个问题:如果多个线程都只是进行读操作,当一个线程在进行读操作时,其他线程只能等待无法进行读操作。
因此就需要一种机制来使得多个线程都只是进行读操作时,线程之间不会发生冲突,通过Lock就可以办到。另外,通过Lock可以知道线程有没有成功获取到锁。这个是synchronized无法办到的。总的来说,也就是说Lock提供了比synchronized更多的功能。
ReadWriteLock读写文件
概述
ReadWriteLock是一个接口,在它里面只定义了两个方法:一个读的锁和一个写的锁。
读的锁:A线程获取了读的锁,那么B线程也可以获取读的锁。
写的锁:A线程获取了写的锁,那么B线程不能获取读也不能获取写的锁。
1 public interface ReadWriteLock { 2 /** 3 * Returns the lock used for reading. 4 * 读的锁,A线程获取了读的锁,那么B线程也可以获取读的锁 5 * @return the lock used for reading. 6 */ 7 Lock readLock(); 8 9 /** 10 * Returns the lock used for writing. 11 * 写的锁,A线程获取了写的锁,那么B线程不能获取读也不能获取写的锁。 12 * @return the lock used for writing. 13 */ 14 Lock writeLock(); 15 }
。。。未完待续