创建多线程的两种方式
继承Thread类的方式
一:实现步骤
- 创建一个继承股Thread的子类
- 重写Thread类的run()方法
- 创建Thread类的子类对象
- 通过子类对象调用strat()开启线程并调用run()方法;
/**
* @author 宇戰天
*
* @description:遍历1-40之内的偶数
*
* @create2020-07-10-16:22
*/
//1.创建一个继承与Thread的子类
class MyThread extends Thread {
//2.重写Thread的run()方法
@Override
public void run() {
for (int i = 0; i < 40; i++) {
if(i%2 ==0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
//3创建Thread的子类对象
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
//4通过此对象实例调用start()方法开启线程
t1.start();
t2.start();
//创建对比主线程
for (int i = 0; i < 40; i++) {
if (i % 2 ==0){
System.out.println(Thread.currentThread().getName()+":"+i+" main");
}
}
}
}
运行结果
线程的常用方法
- start(): 启动线程,并执行对象的run()方法;
- run(): 线程在被调度时执行的操作;
- getName(): 返回线程的名称;
- currentThread:返回执行当前代码的线程;
- setName(String name):设置该线程名称;
/**
* @author 宇戰天
* @description:
* 方式一:
* 设置当前线程的名字setName();
* 为子线程时对象实例.setName(线程名);设置该线程名
* 在main方法中设置线程名时需要先通过currentThread()获取当前代码的线程在设置线程名
* Thread.currentThread().setName("线程名");
* 方式二:
* 在子类对象中添加Thread(String)的构造器在实例化线程时直接初始化线程名
*
* @create2020-07-10-18:42
*/
public class SetNameMethod {
public static void main(String[] args) {
//实例化Thread的子类对象
UpdateThreadName u1 = new UpdateThreadName();
u1.setName("我是子线程");
//子类对象调用start()开启线程并调用器run()方法
u1.start();
//添加主线程对比项
Thread.currentThread().setName("我是主线程");
for (int i = 0; i < 20; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
}
//创建Thread的子类对象
class UpdateThreadName extends Thread{
//提供构造器
public UpdateThreadName() {
}
public UpdateThreadName(String name) {
super(name);
}
//重写run();
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
}
运行结果
6. yield();释放当前cpu 的执行权限
/**
* @author 宇戰天
* @description:
* yield()释放当前CPU的执行权限
* @create2020-07-10-19:03
*/
public class UserYield {
public static void main(String[] args) {
YieldMethod y1 = new YieldMethod();
y1.setName("子线程");
y1.start();
for (int i = 0; i < 50; i++) {
if (i % 2 != 00){
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
}
class YieldMethod extends Thread{
@Override
public void run() {
for (int i = 0;i <100;i++){
if (i % 2 ==0){
System.out.println(Thread.currentThread().getName() + " " + i);
}
if (i ==20){
yield();
}
}
}
}
- join():当某个程序执行流中调用其他线程的join() 方法时,调用线程将被阻塞,直到join() 方法加入的join 线程执行完为止;
- isAlive():返回boolean,判断线程是否还活着
- sleep(long millis):令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队。
/**
* @author 宇戰天
* @description:
* join()方法在线程a中调用线程b的join()方法,此时线程a就进入停止状态知道线程b执行完为止
*sleep();让当前线程休眠,只等时间内为阻塞状态
* isAlive()判断当前线程是否存活
* @create2020-07-10-19:28
*/
public class JoinMethod {
public static void main(String[] args) {
JoinIn method23 = new JoinIn();
method23.setName("线程b");
method23.start();
Thread.currentThread().setName("线程a");
for (int i = 0; i < 50; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i==25){
try {
//在在线程a中调用线程b的join();
method23.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//判断当前线程是否存活
System.out.println(method23.isAlive());
}
}
class JoinIn extends Thread{
@Override
public void run() {
for (int i = 0;i<50;i++){
//当前线程休眠时间
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
线程的优先级
线程的优先级等级
- MAX_PRIORITY:10
- MIN _PRIORITY:1
- NORM_PRIORITY:5(默认优先级)
如何设置线程的优先级 - getPriority() :返回线程优先值
- setPriority(intnewPriority) :改变线程的优先级
注意 - 线程创建时继承父线程的优先级;
- 低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用
创建线程的方式二:实现Runnable接口
实现思路
1.创建一个实现Runnable接口的实现类;
2.实现类实现Runnable中的抽象方法run();
3.创建实现类的对象;
4.将此实现类作为参数传递给Thread的构造器中并创建Thread类的对象;
5.通过Thread类的对象调用start()启动线程并调用Runnable类型的target的run();
/**
* @author 宇戰天
* @description:
* 创建多线程的第二种方式:实现Runnable接口
* 1.创建一个实现Runnable接口的实现类;
* 2.实现类实现Runnable中的抽象方法run();
* 3.创建实现类的对象;
* 4.将此实现类作为参数传递给Thread的构造器中并创建Thread类的对象;
* 5.通过Thread类的对象调用start()启动线程并调用Runnable类型的target的run();
* @create2020-07-10-21:47
*/
//1.创建一个实现Runnable接口的实现类
class Thread2 implements Runnable{
//2.实现类实现Runnable中的抽象方法run();
@Override
public void run() {
for (int i = 0; i < 40; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
public class ImplementsThreadSecondMethod {
public static void main(String[] args) {
//3.创建实现类的对象
Thread2 t2 = new Thread2();
//4.将此实现类作为参数传递给Thread的构造器中并创建Thread类的对象
Thread thread = new Thread(t2);
thread.setName("线程");
//5.通过Thread类的对象调用start()启动线程并调用Runnable类型的target的run();
thread.start();
}
}
线程安全
在java中我们通过同步机制来解决线程的安全问题;解决线程通行常用的三种方法分别为lock锁方式、同步代码块、同步方法
一:同步代码块方式解决线程安全问题
- 定义
synchronized (同步监听器){
//需要被同步的代码
}
- 说明
- 操作共享数据的代码为需要被同步的代码
- 共享数据多个线程共同操作的变量
- 同步监听器:俗称锁任何一个对象都可以充当锁,但是多个线程必须共用同一个锁。
实现Runnable接口方式
/**
* @author 宇戰天
* @description:
*
*通过实现Runnable接口方式创建三个线程实现卖票功能
* 步骤:
* 1.创建一个Runnable接口的类;
* 2.实现Runnable中的抽象方法run();
* 3.创建实现对象
* 4.创建Thread对象,此将实现Runnable的子类对象作为参数传递到Thread的构造器中;
* 5.通过Thread对象调用start()开启线程
*
* 用同步代码块方式解决线程安全问题
* synchronized(监听器) {
*
* 需要同步打代码
* }
* @create2020-07-12-12:31
*/
public class SellTicketTest {
public static void main(String[] args) {
//3.创建实现的对象
TicketWindow chengDu = new TicketWindow();
//4.创建Thread对象,此将实现Runnable的子类对象作为参数传递到Thread的构造器中
Thread sellWindows1 = new Thread(chengDu);
Thread sellWindows2 = new Thread(chengDu);
Thread sellWindows3= new Thread(chengDu);
//设置线程名
sellWindows1.setName("一号窗口");
sellWindows2.setName("二号窗口");
sellWindows2.setName("三号窗口");
//5.通过Thread对象调用start()开启线程
sellWindows1.start();
sellWindows2.start();
sellWindows3.start();
}
}
//1.传建一个实现Runnable接口的子类
class TicketWindow implements Runnable{
//售票数量(多个线程共享此数据)
private int ticket =100;
//定义同步监听器-->锁
Object obj = new Object();
//2.重写Runnable中的抽象方法run()
@Override
public void run() {
while (true){
synchronized(obj) {
if (ticket >0){
//一秒后开启线程为其他线程执行抢占CPU资源提供条件
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 卖票,票的编号为:" + ticket);
ticket--;
}else{
break;
}
}
}
}
}
继承Thread类
**
* @author 宇戰天
* @description:
* 通过继承Thread方式创建三个进程实现卖票功能
* 1.创建继承Thread的子类;
* 2.重写T和read中的run();
* 3.创建Thread的子类对象;
* 4.通过子类对象调用start()方法开启线程
*
* 通过synchronized(同步监视器){
*
* 需要被同步的代码
* }
* 解决线程安全问题
* @create2020-07-12-13:28
*/
public class SellTicketTest1 {
public static void main(String[] args) {
//3.创建Thread类的子类的对象
sellWindows1 t1 = new sellWindows1();
sellWindows1 t2 = new sellWindows1();
sellWindows1 t3 = new sellWindows1();
//更改线程名
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
//4.通过此子类对象调用start()开启线程
t1.start();
t2.start();
t3.start();
}
}
//1.创建一个继承于Thread的子类
class sellWindows1 extends Thread{
//所有对象共享此变量
private static int ticket = 100;
//创建同步锁
private static Object obj = new Object();
//2.重写Thread中的run
@Override
public void run() {
while (true){
synchronized (obj){
if (ticket>0){
//提高其他线程出现的几率
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 卖票,票号为:" + ticket);
ticket--;
}else{
break;
}
}
}
}
}
同步方法解决线程安全问题
实现Runnable接口方式
/**
* @author 宇戰天
* @description:
*
* 同步方法解决线程安全问题
* @create2020-07-12-14:13
*/
public class SellTicketTest3 {
public static void main(String[] args) {
//3.创建实现的对象
TicketWindow3 chengDu = new TicketWindow3();
//4.创建Thread对象,此将实现Runnable的子类对象作为参数传递到Thread的构造器中
Thread sellWindows1 = new Thread(chengDu);
Thread sellWindows2 = new Thread(chengDu);
Thread sellWindows3 = new Thread(chengDu);
//设置线程名
sellWindows1.setName("一号窗口");
sellWindows2.setName("二号窗口");
sellWindows2.setName("三号窗口");
//5.通过Thread对象调用start()开启线程
sellWindows1.start();
sellWindows2.start();
sellWindows3.start();
}
}
//1.传建一个实现Runnable接口的子类
class TicketWindow3 implements Runnable{
//售票数量(多个线程共享此数据)
private int ticket =100;
//2.重写Runnable中的抽象方法run()
@Override
public void run() {
while (true) {
show();
}
}
private synchronized void show(){
if (ticket >0){
//一秒后开启线程为其他线程执行抢占CPU资源提供条件
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 卖票,票的编号为:" + ticket);
ticket--;
}
}
}
继承Thread子类的方式
/**
* @author 宇戰天
* @description:
* @create2020-07-12-14:35
*/
public class sellTicketTest4 {
public static void main(String[] args) {
//3.创建Thread类的子类的对象
sellWindows4 l1 = new sellWindows4();
sellWindows4 l2 = new sellWindows4();
sellWindows4 l3 = new sellWindows4();
//更改线程名
l1.setName("窗口1");
l2.setName("窗口2");
l3.setName("窗口3");
//4.通过此子类对象调用start()开启线程
l1.start();
l2.start();
l3.start();
}
}
//1.创建一个继承于Thread的子类
class sellWindows4 extends Thread{
//所有对象共享此变量
private static int ticket = 100;
//2.重写Thread中的run
@Override
public void run() {
while (true){
show();
}
}
private static synchronized void show(){
if (ticket>0){
//提高其他线程出现的几率
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 卖票,票号为:" + ticket);
ticket--;
}
}
}
使用lock锁方式
/**
* @author 宇戰天
* @description:
* 使用lock方式解决卖票中个线程安全问题
* @create2020-07-12-15:51
*/
//创建实现Runnable接口的实现子类
class windowsLock implements Runnable {
private int ticket = 100;
//实例化ReentrantLock
private ReentrantLock lock1 = new ReentrantLock();
//重写实现子类的run方法
@Override
public void run() {
while(true){
try{
//调用锁定的方法lock()
lock1.lock();
if (ticket >0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 售票,票号为:" + ticket);
ticket--;
}
}finally{
//调用解锁方式
lock1.unlock();
}
}
}
}
public class UseLockRelolveSafelty {
public static void main(String[] args) {
//创建实现子类的对象
windowsLock w2 = new windowsLock();
//将此实现子类对象传给Thread,创建Thread子类
Thread q1 =new Thread(w2);
Thread q2 =new Thread(w2);
Thread q3 =new Thread(w2);
//设置线程名
q1.setName("窗口1");
q2.setName("窗口2");
q3.setName("窗口3");
//调用start开启线程
q1.start();
q2.start();
q3.start();
}
}
线程通信
wait() 与notify() 和notifyAll()
- wait():令当前线程挂起并放弃CPU、同步资源并等待,使别的线程可访问并修改共享资源,而当前线程排队等候其他线程调用notify()或notifyAll()方法唤醒,唤醒后等待重新获得对监视器的所有权后才能继续执行
- notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待
- notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待
注意
这三个方法只有在synchronized方法或synchronized代码块中才能使用
/**
* @author 宇戰天
* @description:
*生产者和消费者问题
* 生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,
* 店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员
* 会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品
* 了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。
* @create2020-07-12-17:49
*/
//被共享的数据
class Clerk{
private int productNum = 0;
//生产产品的方法
public synchronized void addProduct() {
if(productNum<20){
productNum++;
System.out.println(Thread.currentThread().getName() + "开始生产第:" + productNum + "产品");
//唤醒消费者
notify();
}else{
try {
wait();
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 售卖商品的方法
public synchronized void sellProductor() {
if (productNum>0){
System.out.println(Thread.currentThread().getName() + "购买了第" + productNum + "个产品");
productNum--;
//唤醒生产者
notify();
}else{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//生产者线程
class Productor extends Thread{
private Clerk clerk;
public Productor(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "工厂开始生产产品 ......");
while(true){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.addProduct();
}
}
}
//消费者线程
class Customer extends Thread{
private Clerk clerk;
public Customer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始购买产品......");
while(true){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.sellProductor();
}
}
}
public class CustomerAndProduct {
public static void main(String[] args) {
Clerk clerk = new Clerk();
// 创建生产工厂
Productor p1 = new Productor(clerk);
p1.setName("工厂1");
// 创建生产者对象实例
Customer c1 = new Customer(clerk);
c1.setName("消费者1");
Customer c2 = new Customer(clerk);
c2.setName("消费者2");
// 通过对象调用start()方法开启线程
p1.start();
c1.start();
c2.start();
}
}