声明:在创建线程的四种方式中会穿插使用锁的方法来解决线程的安全问题
对锁的解释
* 例子:创建三个窗口买票,总票数为100,用实现接口Runnable的方式 * <p> * 1、问题:买票过程中出现重票、错票--->出现了线程的安全问题 * 2、问题出现的原因:当某个线程在操作车票的过程中,尚未结束,其他线程就参与进来,也操作车票 * 3、如何解决:当一个线程a在操作tickets的时候,其他线程不能参与进来。直到线程a操作完成时,其他线程才能操作ticket, * 这种情况即使线程a出现阻塞,也不能被改变 * 4、在java中、我们通过同步机制,来解决线程的安全问题 * <p> * 方式一:代码同步块 * synchronized(同步监视器){ * //需要被同步的代码--------->不能包多和包少 * } * 说明:1、操作共享想数据的代码,即为需要被同步的代码 * 2、共享数据:多线程共同操作的变量。比如tickets就是共享数据 * 3、同步监视器:俗称:锁;锁:任何一个类的对象都可以充当锁 * 要求:多个线程必须要共享统一把锁。 * * 补充:在实现Runnable接口创建多线程的方式中,我们可以考虑用this来充当同步监视器 * * 方式二:同步方法:将需要被同步的代码包装在方法中:例如实现来完成多线程---private synchronized void show(){//需要被同步的代码}; * 5、同步的方法:解决了线程的安全问题---->好处 * 操作同步代码时:只能有一个线程参与,其他线程等待,相当于一个单线程的过程,效率低----->局限性 *
* 解决线程安全问题的方式三:Lock锁------>JDK5.0新增 * * * Lock(锁) 一般格式: class A{ private final ReentrantLock lock = new ReenTrantLock(); public void m(){ lock.lock(); try{ //保证线程安全的代码; } finally{ lock.unlock(); }}} 注意:如果同步代码有异常,要将unlock()写入finally语句块 * * 1、面试题:synchronized与lock的异同? * 相同:二者都可以解决线程的安全问题 * 不同点:synchronized机制在解决相应的同步代码以后,会自动释放同步监视器 * lock需要手动启动同步(lock()),同时结束同步也需要手动实现(unlock())- * * * 优先使用顺序: Lock 同步代码块(已经进入了方法体,分配了相应资源) 同步方法(在方法体之外)
*
一、通过继承Thread类
创建方法:
1、创建一个继承于Thread的类
2、在改继承类中重写run()方法
3、创建改继承类的对象(即造线程)
4、通过创建的线程调用start()来启动线程并执行重写的run()方法
代码如下:
/**
* //交替打印100以内的偶数
* @author Mr.Wang
* @create 2023-02-09-10:06
*/
class myThread extends Thread{
@Override
public void run() {
synchronized (this.getClass()) {
for (int i = 0; i < 101; i++) {
if (i % 2 == 0) {
System.out.println(currentThread().getName() + "--->" + i);
}
}
}
}
}
public class ThreadTest1 {
public static void main(String[] args) {
myThread myThread = new myThread();
myThread myThread1 = new myThread();
myThread.setName("线程一");
myThread1.setName("线程二");
myThread.start();
myThread1.start();
}
}
二、通过实现Runnable接口
1、创建一个实现Runnable接口的实现类
2、实现(重写)run()方法:将共享数据放入run()
3、创建该实现类对象
4、将创建的实现类对象作为参数放入Thread()中创建线程
5、调用创建的线程中的start()来启动线程并执行run()
代码如下:
package com.atWang.Thread.java;
/**
* 三个窗口卖100张票
* @author Mr.Wang
* @create 2023-02-09-10:17
*/
class myRunnable implements Runnable{
private int tickets=100;
@Override
public void run() {
while(true) {
synchronized (this) {
this.notify();
if (tickets > 0) {
System.out.println(Thread.currentThread() + "----->" + tickets--);
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}else{
break;
}
}
}
}
}
public class WindowsTest {
public static void main(String[] args) {
myRunnable myRunnable = new myRunnable();
Thread thread1 = new Thread(myRunnable);
Thread thread2 = new Thread(myRunnable);
Thread thread3 = new Thread(myRunnable);
thread1.setName("窗口1");
thread2.setName("窗口2");
thread1.start();
thread2.start();
}
}
运行结果:
三、通过实现Callable的方式
1、创建一个实现Callable接口的实现类
2、实现(重写)call()方法:将对共享数据的操作放入call()中
3、创建该实现类的对象
4、将Callable接口实现类的对象作为参数传入FutureTask构造器中,创建FutureTask对象,
5、将FutureTask对象作为参数创建Thread对象,并调用start()
6、如果需要得到返回值,通过创建的FutureTask对象来调用get()来获得实现Call()中的返回值如:Object num=futureTask.get();
package com.atchampion.java3;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 创建线程的方式三:实现Callable接口;------》JDK5.0新增
*
* 对比实现Runnable接口的方式:
* 1、call()可以有返回值
* 2、call()可以抛出异常,被外面的操作捕获,获取异常信息
* 3、Callable可以支持泛型
* @author Mr.Wang
* @create 2023-01-17-11:48
*/
//创建一个实现Callable接口的实现类
class NumTread implements Callable {
//2、实现call方法,将要操作的代码放入实现的call方法中
@Override
public Object call() throws Exception {
int count=0;
for (int i = 0; i < 100; i++) {
if(i%2==0){
System.out.println(i);
count+=i;
}
}
return count;
}
}
public class ThreadNew {
public static void main(String[] args) {
//3、创建实现Callable接口实现类的对象
NumTread numTread = new NumTread();
//4、将Callable接口实现类的对象作为参数传入FutureTask构造器中,创建FutureTask对象,
FutureTask futureTask = new FutureTask(numTread);
//5、将FutureTask对象作为参数创建Thread对象,并调用start()
Thread t1 = new Thread(futureTask);
t1.start();
//6、如果需要得到返回值,通过创建的FutureTask对象来调用get()来获得实现Call()中的返回值如:Object num=futureTask.get();
try {
Object num=futureTask.get();
System.out.println("总和为"+num);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
运行结构:
代码二:
package com.atWang.Thread.java;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import java.util.concurrent.locks.ReentrantLock;
/**
* 实现Callable接口完成多线程
* @author Mr.Wang
* @create 2023-02-09-10:53
*/
class windows implements Callable{
private int tickets=100;
private static ReentrantLock lock= new ReentrantLock(true);//公平锁
@Override
public Object call() {
while(true){
try {
lock.lock();
if(tickets>0){
Thread.sleep(0);
System.out.println(Thread.currentThread().getName()+"------->"+tickets--);
}else{
break;
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
return null;
}
}
public class WindowsTest2 {
public static void main(String[] args) {
windows windows = new windows();
FutureTask windowsFutureTask = new FutureTask(windows);
FutureTask windowsFutureTask1 = new FutureTask(windows);
FutureTask windowsFutureTask2 = new FutureTask(windows);
Thread thread = new Thread(windowsFutureTask);
Thread thread1 = new Thread(windowsFutureTask1);
Thread thread2 = new Thread(windowsFutureTask2);
thread.setName("窗口一:");
thread1.setName("窗口二:");
thread2.setName("窗口三:");
thread1.start();
thread.start();
thread2.start();
}
}
运行结果:
四、通过创建线程池的方法
package com.atchampion.java3;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author Mr.Wang
* @create 2023-01-17-13:27
*/
class NumCount implements Callable{
@Override
public Object call() throws Exception {
for (int i = 0; i < 100; i++) {
if(i%2==0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
return null;
}
}
public class ThreadNew3 {
public static void main(String[] args) {
//提供线程池
ExecutorService service= Executors.newFixedThreadPool(10);
//造实现callable接口的实现类
NumCount numCount = new NumCount();
//FutureTask futureTask = new FutureTask(numCount);//如果想要调用返回值是就创建一个FutureTask类的对象
//将callable接口的实现类作为service.submit参数
service.submit(numCount);
service.submit(new NumCount());
service.submit(new NumCount());
service.submit(new NumCount());
service.submit(new NumCount());
//
service.shutdown();
}
}
运行结果: