多线程
一、实现线程的方式:
方式一:继承Thread类的方式
1.创建一个继承Thread类的子类。
2.重写Thread类的run()–> 将此线程执行的操作声明在run()中
3.创建Tread类的子类的对象
4.通过此对象调用Start()
public class ThreadDemo {
public static void main(String[] args) {
MyThread1 t1=new MyThread1();
t1.start();
}
}
class MyThread1 extends Thread{
@Override
public void run() {
for (int i=0;i<=100;i++){
if(i%2==0){
System.out.println(Thread.currentThread().getName()+":"+ i");
}
}
}
}
问题一:我们启动一个线程,必须调用start(),不能调用run()的方式启动线程。
问题二:如果在启动一个线程,必须重新创建一个Thread子类的对象,调用此对象的start()。
方式二:实现Runable接口的方式
1.创建一个实现了Runable接口的类。
2.实现类去实现Runable中的抽象方法:run()。
3.创建实现类的对象。
4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象。
5.通过Thread类的对象调用start().
class MThtead implements Runnable {
@Override
public void run() {
for (int i=0;i<100;i++){
if (i%2==0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}
public class ThreadTest1 {
public static void main(String[] args) {
MThtead t1=new MThtead();
Thread thread = new Thread(t1);
thread.setName("线程一");
thread.start();
Thread thread1= new Thread(t1);
thread1.setName("线程二");
thread1.start();
}
}
两种方式的对比:
开发中:优先选择:实现Runable接口的方式
原因:1.实现的方式没类的单继承性的局限性
2.实现的方式更适合来处理多个线程共享数据的情况。
相同点:
两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。
目前两种方式,要想启动线程,都是调用的Thread类中的start().
方式三:使用Callable接口和FutrueTask类组合
package cz.oneday.lx;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class CallableTest implements Callable{
@Override
public Object call() throws Exception {
return 5;
}
}
class Main {
public static void main(String[] args){
CallableTest th=new CallableTest();
//也可以直接使用Lambda表达式创建Callable对象
//使用FutureTask类来包装Callable对象
FutureTask<Integer> future=new FutureTask<Integer>((Callable<Integer>)()->{
return 5;
});
new Thread(future,"有返回值的线程").start();//实质上还是以Callable对象来创建并启动线程
try{
System.out.println("子线程的返回值:"+future.get());//get()方法会阻塞,直到子线程执行结束才返回
}catch(Exception e){
e.printStackTrace();
}
}
}
四、方式四:线程池创建
Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序
都可以使用线程池。在开发过程中,合理地使用线程池能够带来3个好处。
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
第三:提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,
还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做到合理利用
线程池,必须对其实现原理了如指掌。
二、操作线程的方法:
package cz.oneday.com;
/**
Thread中的常用方法
1.start():启动当前线程;调用当前线程的run()
2.run():通用需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
3.currentThread(): 静态方法,返回执行当前代码的线程
4.getName():获取当前线程的名字
5.setName():设置当前线程的名字
6.yield():释放当前CPU的执行权
6.join():在线程a中调用线程b的join,此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程吧才结束阻塞状态
7.sleep():线程睡眠
8.isAlive():判断线程是否存活
* */
class HelloThread extends Thread{
@Override
public void run() {
for (int i=0;i<=100;i++){
if(i%2==0){
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+":"+
Thread.currentThread().getPriority()+":"+i);
}
/*
if(i%20==0){
this.yield();
}
*/
}
}
}
public class ThreadMethodTest {
public static void main(String[] args) {
HelloThread t1=new HelloThread();
t1.setName("线程一");
t1.start();
Thread.currentThread().setName("主线程");
for (int i=0;i<=100;i++){
if(i%2==0){
System.out.println(Thread.currentThread().getName()+":"
+ Thread.currentThread().getPriority()+":"+i);
}
if(i==2){
try {
t1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println(t1.isAlive());
}
}
三、线程的同步
1:同步方法
同步方法:使用synchronized修饰的方法,就叫做同步方法,保证一个线程执行该方法的时候,其他线程只能在方法外等着。
package cz.oneday.lx;
class Window1 implements Runnable {
private static int ticket = 100;
Object obj =new Object();
@Override
public void run() {
while (true) {
synchronized (obj) {
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + ticket);
ticket--;
} else {
break;
}
}
}
}
}
public class WindowText1 {
public static void main(String[] args) {
Window1 w=new Window1();
Thread t1=new Thread(w);
Thread t2=new Thread(w);
Thread t3=new Thread(w);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
2:同步代码块。
package cz.oneday.lx;
class Window2 extends Thread {
private static int ticket = 100;
@Override
public void run() {
while (true) {
if (sellTicket() == false) {
break;
}
}
}
public synchronized boolean sellTicket() {
if (ticket <= 0) {
System.out.println(Thread.currentThread().getName() + "票已售罄");
return false;
} else {
ticket--;
System.out.println(Thread.currentThread().getName() + "卖出一张票,还剩" + ticket + "张票");
try {
Thread.currentThread().sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return true;
}
}
}
public class WindowText2 {
public static void main(String[] args) {
Window2 t1=new Window2();
Window2 t2=new Window2();
Window2 t3=new Window2();
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}
3: Lock锁。
同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。
package cz.oneday.lx;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Window3 implements Runnable {
private int ticket = 10;
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
lock.lock();
try {
if (ticket <= 0) {
System.out.println(Thread.currentThread().getName() + "票已售罄");
break;
}
ticket--;
System.out.println(Thread.currentThread().getName() + "卖出一张票,还剩" + ticket + "张票");
try {
Thread.currentThread().sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
lock.unlock();
}
}
}
}
public class WindowText3 {
public static void main(String[] args) {
Window3 window=new Window3();
Thread t1=new Thread(window);
Thread t2=new Thread(window);
Thread t3=new Thread(window);
t1.setName("窗口一");
t2.setName("窗口二");
t3.setName("窗口三");
t1.start();
t2.start();
t3.start();
}
}