多线程
线程的创建和使用
方式一:继承于Thread类
start()的作用:①启动当前线程②调用当前线程的run()
注意:不能直接通过调用run()的方法启动线程,可以再创建一个线程的对象,不可以让已经start()的线程去执行。
class MyThread extends Thread{
public void run(){
//重写的run方法
}
}
public class thread_demo {
public static void main(String[] args) {
MyThread t1=new MyThread();
t1.start();
}
}
方式二:创建Thread类的匿名子类的方式
public class thread_demo {
public static void main(String[] args) {
new Thread(){
public void run(){
//重写的run方法
}
}.start();
}
}
方式三:实现Runnable接口
class MyThread implements Runnable{
public void run(){
//重写的run方法
}
}
public class thread_demo {
public static void main(String[] args) {
MyThread mythread=new MyThread();
Thread t1=new Thread(mythread);
t1.start();
}
}
方式四:JDK5.0新增 实现Callable接口
如何理解Callable比Runnable接口创建多线程方式强大
1.call()可以有返回值
2.call()可以抛出异常,被外面的操作捕获,获取异常的信息
3.call()支持泛型
package callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
//1.创建一个Callable的实现类
class num_thread implements Callable{
@Override
//2.实现call方法,将此线程需要执行的操作声明在call()中
public Object call() throws Exception {
int sum=0;
for (int i = 0; i < 20; i++) {
System.out.println(i);
sum+=i;
}
return sum;
}
}
public class callable_interface {
public static void main(String[] args) {
//3.创建Callable接口实现类的对象
num_thread n=new num_thread();
//4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
FutureTask futureTask = new FutureTask(n);
//5.将FutureTask的对象作为参数,传递到Thread类的构造器中,创建Thread对象,并调用start()方法
new Thread(futureTask).start();
try {
//6.获取Callable中call()方法的返回值
//get()返回值即为FutureTask构造器参数Callable实现类重写的call()返回值
Object sum = futureTask.get();//get方法返回值即为FutureTask构造器参数Callable实现类重写的call()方法的返回值
System.out.println(sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
方式五:使用线程池
Thread的常用方法
start():启动当前线程,调用当前线程的run()。
run():通常需要重写Thread类中的此方法,将创建的线程需要执行的操作声明在此方法中。
currentThread():静态方法,返回执行当前代码的线程。
getName():获取当前线程的名字。
setName():设置当前线程的名字。
yield():释放当前CPU的执行权。
join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,知道线程b完全执行完以后,线程a才结束阻塞状态。
stop():已过时,当执行次方法时,强制结束当前线程。
sleep(long millitime):让当前线程睡眠指定的毫秒。
同步
同步机制,解决线程安全问题
说明:
1.操作共享数据的代码,即为需要被同步的代码.(不能包含代码多了,也不能包含代码少了)
2.有共享数据才有线程安全问题,共享数据:多个线程共同操作的变量。
3.同步监视器,俗称:锁。任何一个类的对象都可以充当锁。
要求:多个线程必须共用同一把锁。
补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。
解决线程安全的方法
方式一:同步代码块
synchronized(同步监视器){
//需要被同步的代码
}
eg1:[用构造的对象当锁] main函数造了三个 window1 对象,构造obj对象时需要用static;
class window1 extends Thread{
private static int ticket=100;
private static Object obj=new Object();
public void run(){
whlie(true){
synchronized(obj){
if(ticket>0){
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(getName()+":"+ticket);
ticket--;
}else{
break;
}
}
}
}
}
public class demo1 {
public static void main(String[] args) {
window1 t1=new window1();
window1 t2=new window1();
window1 t3=new window1();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
eg2:[this当锁] main函数只构造了一个 window2 对象,锁可以用this,此时的this是唯一的window2对象;
class window2 extends Thread{
private static int ticket=100;
public void run(){
whlie(true){
synchronized(this){
if(ticket>0){
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(getName()+":"+ticket);
ticket--;
}else{
break;
}
}
}
}
}
public class demo2 {
public static void main(String[] args) {
window2 w=new window2();
Thread t1=new Thread(w);
Thread t2=new Thread(w);
Thread t3=new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
eg3:[类当锁] 类也是对象(反射)Class clazz=window3.class
class window3 extends Thread{
private static int ticket=100;
private static Object obj=new Object();
public void run(){
whlie(true){
synchronized(window3.class){
if(ticket>0){
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(getName()+":"+ticket);
ticket--;
}else{
break;
}
}
}
}
}
public class demo3 {
public static void main(String[] args) {
window3 w=new window3();
Thread t1=new Thread(w);
Thread t2=new Thread(w);
Thread t3=new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
方式二:同步方法
关于同步方法的总结:
1.同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。
2.非静态的同步方法,同步监视器是:this
静态的同步方法,同步监视器是:当前类本身
eg4:
class window4 extends Thread{
private static int ticket=100;
public void run(){
while(true){
show();
}
}
}
private synchronized void show(){
if(ticket>0){
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(getName()+":"+ticket);
ticket--;
}
}
public class demo3 {
public static void main(String[] args) {
window3 w=new window3();
Thread t1=new Thread(w);
Thread t2=new Thread(w);
Thread t3=new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
eg5:
class window5 implemens Runnable{
private int ticket=100;
public void run(){
while(true){
show();
}
}
}
private static synchronized void show(){
if(ticket>0){
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
} System.out.println(Thread.currentThread().getName()+":"+ticket);
ticket--;
}
}
public class demo3 {
public static void main(String[] args) {
window5 t1=new window5();
window5 t2=new window5();
window5 t3=new window5();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
方式三:Lock锁
JDK 5.0新增
class window extends Thread{
private static int ticket=100;
private ReentrantLock lock=new ReentrantLock();//1.实例化ReentrantLock
public void run(){
whlie(true){
try{
lock.lock();//2.调用lock()方法
if(ticket>0){
try{
Thread.sleep(100);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(getName()+":"+ticket);
ticket--;
}else{
break;
}
}finally{
lock.unlock();//3.调用解锁方法unlock()
}
}
}
}
public class demo3 {
public static void main(String[] args) {
window3 w=new window3();
Thread t1=new Thread(w);
Thread t2=new Thread(w);
Thread t3=new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
synchronized与Lock的异同
1.相同:都可以解决线程安全问题
2.不同:synchronized机制在执行完相应的同步代码块以后,自动释放同步监视器
Lock需要手动的启动同步(Lock()),同时结束同步也需要手动的实现(unlock());
优先使用顺序:Lock->同步代码块(已经进入了方法体,分配了相应资源)->同步方法(在方法体之外)
例题:两个储户分别向同一个账户存3000元,每次存1000,存三次
package test;
class account{
private double balance;
public account(double balance){
this.balance=balance;
}
public synchronized void deposit(double m) {
if (m > 0) {
balance += m;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "存钱成功,余额为:" + balance);
}
}
}
class customer extends Thread{
private account a;
public customer(account a){
this.a=a;
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
a.deposit(1000);
}
}
}
public class account_test {
public static void main(String[] args) {
account a=new account(0);
customer c1=new customer(a);
customer c2=new customer(a);
c1.setName("甲");
c2.setName("乙");
c1.start();
c2.start();
}
}