一、继承于Thread类
1.创建一个继承于Thread类的子类
2.重写Thread类的run(),将此线程的执行操作声明在run()中
3.创建Thread类的子类的对象
4.通过此对象调用start()
class MyThread extends Thread {
@Override
public void run() {
for(int i = 0; i < 100; i++) {
if(i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
}
}
二、Thread的常用方法
/**
* 1.start():启动当前线程:调用当前线程的run()
* 2.run():通常需要重写Thread类的此方法,将创建的线程要执行的操作声明在此方法中
* 3.currentThread():静态方法,返回当前代码执行的线程
* 4.getName():获取当前线程的名字
* 5.setName():设置当前线程的名字
* 6.yield():释放当前cpu的执行权,但还是有可能被分配到
* 7.join():在线程a中调用线程b的join();此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态。
* 8.stop():强制终止线程
* 9.sleep(long millitime):让当前线程睡眠指定的millitime 毫秒。在指定的millitime毫秒时间内,当前线程是阻塞状态
* 10.isAlive():判断当前线程是否活着
*/
class HelloThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
public class ThreadMethodTest {
public static void main(String[] args) {
HelloThread hello = new HelloThread();
hello.setName("线程1");
hello.start();
Thread.currentThread().setName("主线程");
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
if (i == 20) {
try {
hello.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
三、Thread的调度
调度方法:
1.同优先级线程组成先进先出队列,使用时间片策略
2.对高优先级,使用优先调度的抢占式策略
线程优先级:MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5
线程创建时继承父线程的优先级
低优先级只是获得调度的概率低,并非一定是高优先级线程之后才被调用
四、实现Runnable接口
/**
* 1.创建一个实现了Runnable接口的类
* 2.实现类去实现Runnable中的抽象方法:run()
* 3.创建实现类的对象
* 4,将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
* 5.通过Thread类的对象调用start()
*/
class twThread 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) {
twThread tw = new twThread();
Thread thread1 = new Thread(tw);
thread1.start();
}
}
五、线程的生命周期
线程的状态:新建:
当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态。就绪:
处于新建状态的线程被start()后,将进入线程的队列等待CPU时间片,此时它已具备了运行条件,只是没有分配到CPU资源。运行:
当就绪的线程被调度并获得CPU资源时,便进入运行状态,run()方法定义了线程的操作和功能。阻塞:
在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时终止自己的执行,进入阻塞状态。死亡:
线程完成了它的全部工作或线程被提前强制性地终止或出现异常导致结束。
六、线程安全
package com.study.thread;
/**
* 1.出现重票,线程安全问题
* 2.原因:当某个线程操作车票过程中,尚未操作完成时,其他线程进来,也操作车票
* 3.解决:当一个线程a操作ticket时,其他线程不能进来,直到a操作完ticket时,线程才可以开始操作,ticket,这种情况即使线程a出现了阻塞,也不能被改变
* 4,在java中通过同步机制,解决线程安全问题
* 方式一:同步代码块
*
* synchronized(同步监视器) {
* // 需要被同步的代码
* }
*说明:1.操作共享数据的代码,即为需要被同步的代码,不能包含代码多了,也不少
* 2.共享数据:多个线程共同操作的变量,比如ticket
* 3.同步监视器:俗称锁,任何一个类的对象,都可以充当锁
* 要求:多个线程必须要用同一把锁
* 补充:在实现Runnable接口创建多线程方式中,我们可以考虑使用this充当同步监视器
* 继承Thread类创建多线程,慎用this当同步监视器,考虑用当前类充当同步监视器
* 方式二:同步方法
* 如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的
*
* 5.同步的方式:解决了线程安全问题
* 操作同步代码时只能有一个线程参与,其他线程等待,相当于单线程过程,效率低
*/
class Windows implements Runnable {
private int ticket = 100;
Object obj = new Object();
@Override
public void run() {
synchronized(obj) {
while(ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为" + ticket);
ticket--;
}
}
}
}
public class WindowTest {
public static void main(String[] args) {
Windows w1 = new Windows();
Thread t1 = new Thread(w1);
Thread t2 = new Thread(w1);
Thread t3 = new Thread(w1);
t1.start();
t2.start();
t3.start();
}
}
JDK5.0新语法
class Window implements Runnable {
private int ticket = 100;
// 实例化ReentrantLock
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
// 调用lock
lock.lock();
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "售票,票号为:" + ticket);
ticket--;
} else {
break;
}
}finally {
// 解锁
lock.unlock();
}
}
}
}
public class LockTest {
public static void main(String[] args) {
Window window = new Window();
Thread t1 = new Thread(window);
Thread t2 = new Thread(window);
Thread t3 = new Thread(window);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
七、线程通信
package com.study.channel;
/**
* 线程通信:两个线程交替打印1-100数字
* 涉及到3个方法
* wait()一旦执行此方法,当前线程进入阻塞状态,并释放同步监视器
* notify():一旦执行此方法,就会唤醒被wait()的一个线程,如果有多个线程被wait,就唤醒优先级高的
* notifyall()一旦执行此方法,就会唤醒所有wait的线程
*
* 说明:
* 这三个方法必须放到同步代码块或同步方法中
* 这三个方法的调用者必须是同步代码块或同步方法中的同步监视器,否则会出现异常
* sleep() 和 wait()的异同
* 相同点:一旦执行方法, 都可以使得当前线程进入阻塞状态
* 不同点:
* 两个方法声明的位置不同:Thread类中声明sleep()方法,Object类中声明wait()方法
* sleep()可在任何场景下调用,wait()必须在同步代码块中调用
* 如果两个方法都在同步代码块中调用,sleep()不会释放同步监视器,wait()会释放同步监视器(锁)
*
*/
class Number implements Runnable {
private int number = 100;
@Override
public void run() {
while (true) {
synchronized (this) {
notifyAll();
if (number >= 1) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + number);
number--;
try {
// 使得调用如下wait如下方法的线程进入阻塞状态
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
}
}
}
public class CommuncationTest {
public static void main(String[] args) {
Number num = new Number();
Thread t1 = new Thread(num);
Thread t2 = new Thread(num);
Thread t3 = new Thread(num);
t1.setName("线程1");
t2.setName("线程2");
t3.setName("线程3");
t1.start();
t2.start();
t3.start();
}
}
八、实现Callable创建多线程
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 比实现Runnable强大
* 1.支持返回值
* 2.支持抛异常
* 3.泛型
*/
// 1. 创建一个实现Callable的实现类
class NumThread implements Callable {
// 实现call 方法,将线程要做的操作生命在call方法中
@Override
public Object call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0) {
sum += i;
}
}
return sum;
}
}
public class ThreadNew {
public static void main(String[] args) {
// 3.创建callable 实现类的对象
NumThread num = new NumThread();
// 4.将此Callbale 实现类的对象传递到FutureTask构造器中,创建FutureTask对象
FutureTask futureTask = new FutureTask(num);
// 5. 将futureTask传入Thread构造器
new Thread(futureTask).start();
try {
// 6. 获取到相应返回值
Object sum = futureTask.get();
System.out.println("返回值:" + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
九、线程池创建多线程
package com.study.thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 使用线程池创建线程
* 好处:
* 1.提升响应速度,
* 2.降低资源消耗(重复利用线程池中的线程,不需要每次都创建)
* 3。便于点成管理
* corePloolSize 核心池的大小
* maxinumPloolSize 最大线程数
* keepAliveTime 线程没有任务时,最多保存多长时间终止
*/
class NumberThread 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 ThreadPool {
public static void main(String[] args) {
// 1.提供指定线程数量的线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
//2.执行指定的线程操作,需要提供实现Runnable接口或者Callable 接口实现类的对象
executorService.execute(new NumberThread()); // 适用于Runnable
// executorService.submit(Callable callable) // 适用于Callable
//3.关闭连接池
executorService.shutdown();
}
}