一: java多线程的四种实现方式
注:创建线程的对象只代表该线程,并不是线程本身。线程本身是一条运行的子程序
1:通过继承Thread类,重写run()方法
继承Thread类,实现一个自定义的线程类,重写类中的run()方法,通过调用自定义线程类的构造方法创建一个线程,通过调用自定义线程类的start()方法,启动一个线程。启动后,线程自动调用run()方法运行线程。
package hhxy;
class MyThread extends Thread {
private int sum;
public MyThread(int sum, String name) {
super(name);
this.sum = sum;
}
@Override
public void run() {
while (sum > 0)
System.out.println(Thread.currentThread().getName() + "卖出编号:" + (sum--) + "的票");
}
}
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThread my = new MyThread(20, "lwk");
my.start();
}
}
2:实现Runnable接口,实现run()方法
新建一个自定义类实现Runable接口,实现Runnable接口中的run()方法。创建线程时,先创建一个自定义的类的对象 ,创建一个Thread类的对象,将自定义类的对象作为参数传入Thread类的构造方法中。通过调用 Thread对象的 start()方法启动线程,自动调用Thread类对象的run()方法运行线程。Thread()类对象的run()方法自动调用自定义类run() 方法。
package hhxy;
class MyRunnable implements Runnable {
private int sum;
public MyRunnable(int sum) {
this.sum = sum;
}
@Override
public void run() {
while (sum > 0)
System.out.println(Thread.currentThread().getName() + "卖出编号:" + (sum--) + "的票");
}
}
public class Main {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyRunnable myable = new MyRunnable(20);
Thread my = new Thread(myable, "lwk");
my.start();
}
}
3:实现Callable接口,实现call()方法
(1) :Callable封装一个异步运行的任务,Callable接口与Runable接口类似,但是Callable有返回值,Callable是一个参数化的类型,只有一个方法call(),类型参数是返回值的类型。例如Callable<Integer>返回一个Integer对象。
(2):Future保存异步运算的结果可以启动一个计算,将Future对象交给某个线程,然后忘掉它,Future对象的所有者在结果计算好以后就可以获得它。
(3):FutureTask包装器是个非常便利的机制,可将Callable转换成Future和Runable,它同时实现这两个接口
(4):创建线程时,先创建一个实现Callable接口的自定义类的对象,然后将此对象封装到FutureTask中,FutureTask便能作为Runable和Future使用。启动线程时仍然调用start()方法。
package hhxy;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
class MyCallable implements Callable<Integer> {
private int sum;
public MyCallable(int sum) {
this.sum = sum;
}
@Override
public Integer call() throws Exception {
// TODO Auto-generated method stub
Integer add = 0;
for (int i = 1; i <= sum; i++) {
add = i + add;
}
return add;
}
}
public class Main {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// TODO Auto-generated method stub
MyCallable mycall = new MyCallable(20);
FutureTask<Integer> task = new FutureTask<Integer>(mycall);
Thread my = new Thread(task);
my.start();
Integer result = task.get();
System.out.println(result);
}
}
4:利用线程池是创建多线程
(1):创建线程是需要代价的,如果每次创建的都是生存周期很短的线程,那太浪费资源了,此时便需要使用线程池,一个线程池中包含许多准备运行的空闲线程,将Runnable对象交给线程池,就会有一个线程调用run()方法。当run()方法退出时线程不会死亡,而是在池中准备为下一个请求服务。
(2):执行器(Executor)类有很多静态方法来构建线程池。
方法 | 描述 |
newCachedThreadPool() | 必要时创建新线程,空闲线程会被保留60秒 |
newFixedThreadPool(int n) | 该池包含固定数量n的线程,空闲线程会一直被保留 |
newSingleThreadExecutor() | 只有一个线程,该线程顺序执行每一个提交的任务 |
newScheduledThreadPool() | 用于预定执行而构建的固定线程池,替代java.util.Timer |
newSingleThreadScheduledEcecutor() | 用于预定执行而构建的单线程"池” |
(3):前三种方法返回了实现ExecutorService接口的ThreaPoolExecutor类的对象。可用下面的方法将一个Runnable对象,或者Callable对象题提交给ExecutorService
方法 | 描述 |
Future<?> submit(Runnable task) | 返回一个Future<?>对象,使用这个对象调用Future类的方法,但是调用get()时返回null |
Future<T> submit(Runnable task,result) | 返回的Future<T>对象,调用get()时返回result |
Future<T> submit(Callable<T> task) | 传入Callable<T>,返回的Future将在计算结果完成后得到它 |
package hhxy;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
class MyCallable implements Callable<Integer>{
private int sum;
public MyCallable(int sum)
{
this.sum=sum;
}
public Integer call() throws Exception {
// TODO Auto-generated method stub
Integer add=0;
for(int i=1;i<=sum;i++)
{
add=i+add;
}
return add;
}
}
public class Main {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService es= Executors.newFixedThreadPool(10);
MyCallable myCall=new MyCallable(20);
Future<Integer> future=es.submit(myCall);
System.out.println(future.get());
}
}
二:java的两种线程锁
线程锁的存在是为了线程安全,当多个线程访问一块共享资源时,就会因为线程之间无序的随意访问造成无法预估的错误,因此引入了线程锁,即对象中的被上锁一块资源只能被拿到锁的线程访问,即一次只能被一个线程访问。当上一个线程访问结束后,释放锁,下一个线程才能重新拿到锁访问该资源。以下代码便是线程不安全的
package hhxy;
class MyRunnable implements Runnable{
private int sum;
public MyRunnable(int sum)
{
this.sum=sum;
}
@Override
public void run() {
while(sum>0)
{
try {
Thread.sleep(500); //通过延时放大错误
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖掉编号:"+(sum--)+"的票");
}
}
}
public class Main {
public static void main(String[] args) {
MyRunnable myRun=new MyRunnable(20);
Thread lwk = new Thread(myRun,"lwk");
Thread lz=new Thread(myRun,"lz");
lwk.start();
lz.start();
}
}
可以看到有些编号的票被重复卖出,而且次序混乱,因此线程不安全,故引出了锁机制
1:内部对象锁(synchronized关键字)
每一个对象都有一个内部锁,被关键字synchronized声名的方法或者代码块如果被一个线程访问,那么该线程必须要获得对象的内部锁,synchronized的声名有以下两种方式
(1):声名方法:public synchronized void method( ...)
(2):声名代码块 synchronized (this|Object|Class.class)
package hhxy;
import java.io.SyncFailedException;
class MyRunnable implements Runnable{
private int sum;
public MyRunnable(int sum)
{
this.sum=sum;
}
@Override
public void run() {
while(sum>0)
{
synchronized (this) {
try {
Thread.sleep(500); //通过延时放大错误
} catch (InterruptedException e) {
e.printStackTrace();
}
if(sum<=0)
break;
System.out.println(Thread.currentThread().getName()+"卖掉编号:"+(sum--)+"的票");
}
}
}
}
public class Main {
public static void main(String[] args) {
MyRunnable myRun=new MyRunnable(20);
Thread lwk = new Thread(myRun,"lwk");
Thread lz=new Thread(myRun,"lz");
lwk.start();
lz.start();
}
}
由图可知,synchronized 关键字声明后线程安全
2:锁对象(ReentrantLock)
(1):锁对象的应用
java SE 5.0以后引入了ReentrantLock类,该类的对象提供一个显式锁,基本结构如下:
try{
myLock.lock();
.......
}
finally{
unLock();
}
当该锁对象调用lock()方法时,代码块被上锁,当前线程取得锁,其他线程因没有锁,因此无法通过lock(),直到该锁对象调用unLock()方法,当前线程才会释放锁。因此必须要手动释放锁。
package hhxy;
import java.io.SyncFailedException;
import java.util.concurrent.locks.ReentrantLock;
class MyRunnable implements Runnable{
private int sum;
private ReentrantLock myLock;
public MyRunnable(int sum)
{
this.sum=sum;
myLock=new ReentrantLock();
}
@Override
public void run() {
while(sum>0)
{
try {
Thread.sleep(500); //延时放大错误
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
myLock.lock();
try {
if(sum<=0)
break;
System.out.println(Thread.currentThread().getName()+"卖掉编号:"+(sum--)+"的票");
}
finally {
myLock.unlock();
}
}
}
}
public class Main {
public static void main(String[] args) {
MyRunnable myRun=new MyRunnable(20);
Thread lwk = new Thread(myRun,"lwk");
Thread lz=new Thread(myRun,"lz");
lwk.start();
lz.start();
}
}
运行结果与内部对象锁(synchronized关键字)相似,不再贴出运行结果
2:条件对象(Condition)
当线程进入临界区(被锁的资源)却发现只有符合某条件才能继续运行,此时便需要一个条件对象来管理这些线程。一个锁对象可以有一个或多个条件对象,可以用一个newCondition()方法获得条件对象。
条件对象的方法:
方法 | 描述 |
await() | 将此线程放入条件对象的等待集中 |
signalAll() | 解除该条件对象等待集中的所有线程的阻塞状态 |
signal() | 从条件对象的等待集的线程中随机选择一个线程,解除其阻塞状态 |
package hhxy;
import java.io.SyncFailedException;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
class MyRunnable implements Runnable{
private int sum;
private ReentrantLock myLock;
private Condition con;
public MyRunnable(int sum)
{
this.sum=sum;
myLock=new ReentrantLock();
con=myLock.newCondition();
}
@Override
public void run() {
while(sum>0)
{
try {
Thread.sleep(500); //延时放大错误
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
myLock.lock();
try {
if(sum<=0)
break;
if(sum%4==0&&Thread.currentThread().getName().equals("lwk"))
{
con.await();
}
System.out.println(Thread.currentThread().getName()+"卖掉编号:"+(sum--)+"的票");
con.signalAll();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally {
myLock.unlock();
}
}
}
}
public class Main {
public static void main(String[] args) {
MyRunnable myRun=new MyRunnable(20);
Thread lwk = new Thread(myRun,"lwk");
Thread lz=new Thread(myRun,"lz");
lwk.start();
lz.start();
}
}
三:线程的五种状态
1:新建状态
线程刚刚被创建
2:可运行状态
调用start方法,但还未运行run()方法
3:运行状态
运行run()方法
4:阻塞状态
(1)同步阻塞:当线程访问一个共享资源时,该对象的锁被另一个线程获取而还未释放,此时该线程将进入该对象的锁池中等待锁被释放,此时造成同步阻塞
(2)等待阻塞:当一个线程中某对象调用wait()方法时,该线程进入到该对象的等待池中,等待另一个访问该对象的线程调用notify()方法或者notifyAll()重新唤醒,此时造成等待阻塞,如不能及时唤醒,可能照成死锁。
(3)其他阻塞:当线程调用sleep()方法或者join()方法时
5:死亡状态
run()方法正常退出,或异常中断,线程死亡
四:一些易混方法
1:sleep(int n)/yield()
两个方法都是静态方法,在哪个线程中调用,作用于哪个线程。sleep(int n)方法使线程休眠n毫秒,线程不释放当前对象的锁,造成阻塞。yield()方法使线程暂停一下,放弃CPU资源但是不释放锁,yield()方法将执行机会让给高于或等于它优先级的线程,如没有符合条件的线程,则该线程继续执行
package hhxy;
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread.sleep(500);
Thread.yield();
System.out.println("hello");
}
}
2:wait()/notify()/notifyAll()和await()/signal()/signalAll()
(1):一个对象调用wait()方法,访问当前对象的线程被放入该对象的等待池中,线程释放锁,等待下一个访问该对象的线程调用notify()/notifyAll()方法,将该线程从等待池中唤醒。
package hhxy;
import java.io.SyncFailedException;
class Sale{
private int sum=20;
public void sale() throws InterruptedException
{
while(sum>0)
{
synchronized (this) {
if(sum<=0)
break;
if(sum%4==0&&Thread.currentThread().getName().equals("lwk"))
wait();
Thread.sleep(500);
System.out.println(Thread.currentThread().getName()+"卖出编号:"+(sum--)+"的票");
this.notifyAll();
}
}
}
}
class MyRunnable implements Runnable{
private Sale sale=new Sale();
public void run() {
try {
sale.sale();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class Main {
public static void main(String[] args) {
MyRunnable myRun=new MyRunnable();
Thread lwk = new Thread(myRun,"lwk");
Thread lz=new Thread(myRun,"lz");
lwk.start();
lz.start();
}
}
(2):一个条件对象调用await()方法,访问当前条件对象的线程被放入该条件对象的等待池中,线程释放锁,等待下一个线程调用同一条件对象notify()/notifyAll()方法,将该线程从等待池中唤醒。
package hhxy;
import java.io.SyncFailedException;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
class Sale{
private int sum=20;
private Condition con;
private ReentrantLock lock =new ReentrantLock();
public void sale() throws InterruptedException
{
while(sum>0)
{
try {
lock.lock();
con=lock.newCondition();
if(sum<=0)
break;
if(sum%4==1&&Thread.currentThread().getName().equals("lwk"))
con.await();
Thread.sleep(500);
System.out.println(Thread.currentThread().getName()+"卖出编号:"+(sum--)+"的票");
con.signalAll();
}
finally {
lock.unlock();
}
}
}
}
class MyRunnable implements Runnable{
private Sale sale=new Sale();
public void run() {
try {
sale.sale();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class Main {
public static void main(String[] args) {
MyRunnable myRun=new MyRunnable();
Thread lwk = new Thread(myRun,"lwk");
Thread lz=new Thread(myRun,"lz");
lwk.start();
lz.start();
}
}
运行结果与wait()/notify()/notifyAll()相似,不再贴出运行结果
3:join()
在一个当前线程中,由另一个子线程调用join()方法,当前线程释放锁,当前线程必须要等待子线程执行完毕后才能继续执行。
(1):未采用join()方法
package hhxy;
import java.util.Scanner;
class MyRunnable implements Runnable{
private String str="";
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Scanner in=new Scanner(System.in);
System.out.println("输入字符串");
str=in.next();
}
public String getStr() {
return str;
}
}
public class Main {
public static void main(String[] args) {
MyRunnable myRun=new MyRunnable();
Thread my = new Thread(myRun);
my.start();
System.out.println("我是主线程"+myRun.getStr());
}
}
(2):采用join()方法
package hhxy;
import java.util.Scanner;
class MyRunnable implements Runnable{
private String str="";
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Scanner in=new Scanner(System.in);
System.out.println("输入字符串");
str=in.next();
}
public String getStr() {
return str;
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
MyRunnable myRun=new MyRunnable();
Thread my = new Thread(myRun);
my.start();
my.join();
System.out.println("我是主线程"+myRun.getStr());
}
}
五:何为死锁
所有的的线程都处于等待阻塞状态,等待其他的线程去唤醒自己,但是已经不存在正在运行的线程去唤醒它们,此时便会造成死锁,而程序不结束运行,无限等待下去。
package hhxy;
import java.io.SyncFailedException;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
class Sale{
private int sum=20;
private Condition con;
private ReentrantLock lock =new ReentrantLock();
public int getSum() {
sum--;
return sum;
}
public void sale() throws InterruptedException
{
while(sum>0)
{
synchronized (this) {
if(sum<=0)
break;
if(sum%4==1)
wait();
Thread.sleep(500);
System.out.println(Thread.currentThread().getName()+"卖出编号:"+(sum--)+"的票");
}
}
}
}
class MyRunnable implements Runnable{
private Sale sale=new Sale();
public void run() {
try {
sale.sale();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class Main {
public static void main(String[] args) {
MyRunnable myRun=new MyRunnable();
Thread lwk = new Thread(myRun,"lwk");
Thread lz=new Thread(myRun,"lz");
lwk.start();
lz.start();
}
}