方式一:继承于Thread类
* 1.创建一个继承于Thread类的子类
* 2.重写Thread类的run()方法(将此线程的操作声明在方法中)
* 3.创建Thread类的子类的对象
* 4.通过此对象调用start()方法
//例子:遍历100以内所有的偶数
//1.创建一个继承于Thread类的子类
class myThread extends Thread {
//2.重写Thread类的run()方法
@Override
public void run() {
//重写Thread类的run()方法
for (int i = 0;i<=100;i++){
if(i % 2 == 0){
System.out.println(i);
System.out.println(Thread.currentThread().getName() + i);
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
//3创建Thread类的子类的对象
myThread t1 = new myThread();
//4.通过此对象调用start()方法;①启动该当前线程,②调用当前线程的run()方法
t1.start();
System.out.println("hello");
//问题一:我们不能通过直接调用run()的方法启动线程
//t1.run();
//问题二:再启动一个线程,遍历100以内的偶数,不可以还让已经start的线程去执行
//会报:IllegalThreadStateException
//我们需要重新创建一个线程对象,并调用start方法
t1.start();
//如下的操作仍然是在main线程中执行的
for (int i = 0;i<=100;i++){
if(i % 2 == 0){
System.out.println(i+"****main*****");
}
}
}
}
方式二:创建实现Runnable接口的类
* 1.创建一个实现Runnable接口的类
* 2.实现类去实现Runnable中的抽象方法:run();
* 3.创建实现类的对象
* 4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
* 5.通过Thread类的对象调用start();
//1.创建一个实现Runnable接口的类
class Mthread implements Runnable {
//2.实现类去实现Runnable中的抽象方法:run();
@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) {
//3.创建实现类的对象
Mthread mthread = new Mthread();
//4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
Thread t1 = new Thread(mthread);//原构造器形参是Runable接口对象,这里使用的接口实现类的对象,构成多态
//5.通过Thread类的对象调用start();①启动线程②调用当前线程的run()-->调用了Runable类型的target的run(),
// target通过构造器被赋值为mthread
t1.setName("线程1");
t1.start();
//再启动一个线程,遍历100以内的偶数
Thread t2 = new Thread(mthread);
t2.setName("线程2");
t2.start();
}
方式三:实现Callable接口 -->JDK 5.0新增
面试题:如何理解实现Callable接口比实现run接口创建多线程强大?
1.call()方法有返回值 ;
2.call()方法抛出异常 ;
3.callable接口是支持泛型的;
接口: interface Callable{} -----------------interface Runnable{}
实现类: class NewThread{} ————> class FutherTest{} ————> class Thread{}.start()
//1.创建一个实现callable的实现类
class NumberThread implements Callable{
//实现call()方法的重写,将此线程执行的操作声明在call()
@Override
public Object call() throws Exception {//回调方法
int sum = 0;
for (int i = 1; i <= 100; i++) {
if (i%2==0){
System.out.println(i);
sum+= i;
}
}
return sum;//也可以return null
}
}
public class ThreadNew {
public static void main(String[] args) {
//3.创建Callable接口实现类的对象
NumberThread numberThread = new NumberThread();
//4.将此Callable实现类的对象作为参数传递到FutureTask构造器的,创建FutureTask对象
FutureTask futureTask = new FutureTask(numberThread);
//将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象并调用start方法
new Thread(futureTask).start();//futureTask也实现了runable接口
try {
//6.获取Callable中call方法的返回值
//get()的返回值就是FutureTask构造器参数Callable实现类重写的call()方法的返回值
Object sum = futureTask.get();//get的目的是获取call方法的返回值,可以不调
System.out.println("总和为:"+sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
方式四:使用线程池
好处:
* 1.提高响应速度(减少了创建线程的时间)
* 2.降低资源消耗(重复利用线程池中的线程,不需要每次都新创建)
* 3.便于线程管理(调用下列方法)
* corePoolSize:核心池的大小
* maximumpoolSize:最大线程数
* keepAliveTime:线程没有任务时最多保持多长时间会终止
* 面试题:创建线程有几种方式?四种
* 1、继承Thread()类
* 2、实现Run()接口
* 3、实现Callable接口 -->JDK 5.0新增
* 4、使用线程池
execute:方法
Executors:创建线程池的工具类
class NumberThread1 implements Runnable{//实现线程的功能
@Override
public void run() {
for (int i = 0; i <= 100 ; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName() +":"+ i);
}
}
}
}
class NumberThread2 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 service = Executors.newFixedThreadPool(10);//service是ExecutorService接口的引用,是常量
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;//向下强转成实现类的类型的对象
//设置线程池的属性
service1.setCorePoolSize(10);
service1.setKeepAliveTime(10);
//2.执行指定的线程操作,需要提供实现Runna接口或者Callable接口实现类的对象
NumberThread1 numberThread1 = new NumberThread1();
NumberThread2 numberThread2 = new NumberThread2();
service.execute(numberThread1);//适合使用于Runnable
service.execute(numberThread2);
service.submit(Callable callable);//适合是用于Callable
//3.关闭连接池
service.shutdown();
}
}
线程的生命周期
线程的通信
线程通信的例子,使用两个线程打印1-100,线程1 线程2交替打印
* wait();一旦执行此方法,当前线程进入阻塞状态,并释放同步监视器;
* notify();一旦执行此方法,就会唤醒wait的一个线程,如果多个线程wait状态,则唤醒优先级高的那个;
* notifyAll();一旦执行此方法,唤醒所有被wait的线程;
* 说明:
* 1.wait(),notify(),notifyAll()三个方法必须使用在synchronized同步代码块或者同步方法中.
* 2.wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。 否则会出现IllegalMonitorStateException异常
* 3.wait(),notify(),notifyAll()三个方法均为定义在java.lang.Object类中(方便任意一个对象当同步监视器时可以调用这个方法)。
*
* 面试题:sleep()和wait()方法的异同
相同点:一旦执行方法都可以使得当前线程进入阻塞状态
不同点:1)两个方法声明的位置不同:Thread类中声明的sleep(),Objet类中声明的wait();
* 2)调用的范围不同:sleep()可以在任何需要的场景调用Thread.sleep(100)静态方法,
* wait()必须同步监视器才能调用,即必须使用在同步方法或者同步代码块中。
* 3)关于是否释放同步监视器;如果两个方法都使用在同步代码块或者同步方法中,sleep()
* 不会释放同步监视器,wait()会释放。
class Number implements Runnable {
private int numeber = 1;
private Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj) {//Number的对象充当同步监视器
obj.notify();//唤醒阻塞的 线程
if (numeber <= 100) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + numeber);
numeber++;
try {
obj.wait();//执行wait会释放锁,使得线程2得以拿到锁进入
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
}
}
}
public class CommunicationTest {
public static void main(String[] args) {
Number number = new Number();
Thread t1 = new Thread(number);
Thread t2 = new Thread(number);
t1.setName("甲");
t2.setName("乙");
t1.start();
t2.start();
}
}
线程通信的应用:生产者/消费者问题
生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,如果店中有空位放产品了再通知生产者继续生产:如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品。
这里可能出现两个问题:
>生产者比消费者快时,消费者会漏掉一些数据没有取到。
>消费者比生产者快时,消费者会取相同的数据。
分析
1.是否多线程问题?是,生产者线程、消费者线程
2.是否有共享数据?是,店员、产品数量
3.如何来解决线程安全问题?同步机制,有三种方法
4.是否涉及到线程的通信?是
class Clerk{//店员
private int productNumber = 0;
//生产产品
public synchronized void produceProduct() {
if (productNumber<20){
productNumber++;
System.out.println(Thread.currentThread().getName()+":开始生产第"+productNumber+"个产品");
notify();//唤醒消费者 来消费
}else{
try {
wait();//等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//消费产品
public synchronized void consumeProduct() {
if (productNumber>0){
System.out.println(Thread.currentThread().getName() +"开始消费第"+productNumber + "个产品");
productNumber--;
notify();//唤醒生产者来生产
}else{
try {
wait();//等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Producer extends Thread{//生产者
private Clerk clerk;
@Override
public void run() {
System.out.println(getName()+ ":"+"开始生产产品");
while (true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.produceProduct();
}
}
public Producer(Clerk clerk) {
this.clerk = clerk;
}
}
class Customer extends Thread{//消费者
private Clerk clerk;
public Customer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(getName()+ ":"+"开始消费产品");
while (true){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.consumeProduct();
}
}
}
public class ProductTest {
public static void main(String[] args) {
Clerk clerk = new Clerk();//唯一的 Clerk对象,作为同步监视器
Producer p1 = new Producer(clerk);
p1.setName("生产者1");
Customer c1 = new Customer(clerk);
c1.setName("消费者1");
Customer c2 = new Customer(clerk);
c2.setName("消费者2");
p1.start();
c1.start();
c2.start();
}
}