1.多线程概述
进程:一个内存中运动的应用程序,每个进程都有一个独立的内存空间。
线程:进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程至少有一个线程。
同步:排队执行,效率低但是安全。
异步:同时执行,效率高但数据不安全。
并发:两个或多个事件在同一时间段内发生。
并行:两个或多个事件在同一时刻发生。
线程的6种状态:New(线程尚未启动),Runnable(正在执行),Blocked(线程被阻塞),Waiting(无限等待),TimedWaiting(指定时间等待),Terminated(线程中断)
2.多线程的实现
2.1继承Thread
通过重写Thread的run方法执行任务。
package com.chapter.four.five;
public class Demo {
public static void main(String[] args) {
Thread t = new MyThread();//创建线程对象
t.start();//开始线程
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class MyThread extends Thread{
public void run(){
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+i);//当前线程的名字
try {
Thread.sleep(1000);//休眠1s
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
2.2实现Runnable
与继承Thread相比的优点:
1.通过创建任务然后给线程进行分配的方式实现多线程更适合多个线程同时执行相同任务。
2.可以避免单继承带来的局限性。
3.任务与线程本身分离,提升了程序的健壮性
4.线程池技术接受Runnable类型任务,不接受Thread
package com.chapter.four.five;
public class Demo {
public static void main(String[] args) {
/**
* 1.创建任务对象2.创建一个线程并为其分配一个任务3.执行这个线程
*/
Runnable r = new MyRunnable();
Thread t = new Thread(r);
t.start();//开始线程
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class MyRunnable implements Runnable{
public void run(){
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+i);//当前线程的名字
try {
Thread.sleep(1000);//休眠1s
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
2.3带返回值的Callable
该方法创建多线程比较繁琐,不经常使用。使用步骤为
1.编写类实现Callable接口,实现call方法
2.创建FutureTask对象,并传入第一步编写的Callable类对象
3.通过Thread启动线程
package com.chapter.four.five;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test3 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<Integer> c = new MyCallable();
//2.创建FutureTask对象,并传入第一步编写的Callable类对象
FutureTask task = new FutureTask(c);
//3.通过Thread启动线程
new Thread(task).start();
Integer j = (Integer)task.get();//等到收到返回值才会向下执行代码
for(int i=0;i<10;i++){
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//1.编写类实现Callable接口,实现call方法
static class MyCallable implements Callable<Integer>{
@Override
public Integer call() throws Exception {
for(int i=0;i<10;i++){
System.out.println(i);
Thread.sleep(1000);
}
return 100;
}
}
}
3.线程安全问题
先来看一段代码,在下面这段代码中会出现余票为负数的情况,这是因为当线程1在休眠的时候,其他线程进入将票的数据已经修改,出现了线程不安全的问题。
package com.chapter.four.five;
public class Demo {
public static void main(String[] args) {
/**
* 1.创建任务对象2.创建一个线程并为其分配一个任务3.执行这个线程
*/
Runnable r = new Ticket();
//同时开启三个窗口进行卖票
new Thread(r).start();
new Thread(r).start();
new Thread(r).start();
}
static class Ticket implements Runnable{
private int count = 10;//初始化票的张数为10张
public void run(){
while(count>0){
System.out.println(Thread.currentThread().getName()+"正在卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;//票数减1
System.out.println("余票为"+count);
}
}
}
}
为了解决线程不安全的问题,目前有三种方法。
3.1同步代码块
格式:synchronized(锁对象用于锁代码){}
package com.chapter.four.five;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test{
public static void main(String[] args) {
Ticket run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
private int count = 10;
private Object o = new Object();
@Override
public void run() {
while (true){
synchronized (o){
boolean b = sale();
if(!b){
break;
}
}
}
}
public boolean sale(){
if(count>0){
System.out.println(Thread.currentThread().getName()+"正在卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("余票为:"+count);
return true;
}
return false;
}
}
}
3.2 同步方法
用synchronized修饰方法
package com.chapter.four.five;
public class Test{
public static void main(String[] args) {
Ticket run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
private int count = 10;
@Override
public void run() {
while (true){
boolean b = sale();
if(!b){
break;
}
}
}
public synchronized boolean sale(){
if(count>0){
System.out.println(Thread.currentThread().getName()+"正在卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("余票为:"+count);
return true;
}
return false;
}
}
}
3.3显示锁Lock
利用ReentrantLock类的lock和unlock方法。
package com.chapter.four.five;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test{
public static void main(String[] args) {
Ticket run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}
static class Ticket implements Runnable{
private int count = 10;
private Lock l = new ReentrantLock();
@Override
public void run() {
while (true){
l.lock();
boolean b = sale();
if(!b){
break;
}
l.unlock();
}
}
public boolean sale(){
if(count>0){
System.out.println(Thread.currentThread().getName()+"正在卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("余票为:"+count);
return true;
}
return false;
}
}
}