java 多线程登录_java多线程系列(1)

1,为什么需要线程?

作用:提升cpu的利用率,如,早期的dos系统,执行2个命令时( command 1, command 2 ),如果command1【假如是磁盘遍历文件的IO操作】执行的时间比较长,那么command 2必须等待,这种方式就是同步阻塞,

cpu就闲置了,为了提高cpu的利用率,我们就要使用多线程,如果一个任务时间比较长,cpu就暂时挂起他,去执行另外的线程,所以线程一般是异步的。

2,每一个进程至少会有一个线程在运行

public classTest {public static voidmain(String[] args) {//打印线程的名称

System.out.println( Thread.currentThread().getName() );

}

}

输出结果为 "main" ,注意这个main是线程的名字,跟main函数的名字相同而已。

3,在java中实现多线程有2种方式

>继承Thread类

>实现Runnable接口

在run方法中写线程要执行的任务

class MyThread extendsThread{public voidrun(){

System.out.println("MyThread::run");

}

}public classThreadUse1 {public static voidmain(String[] args) {

MyThread mt= newMyThread();

mt.start();

System.out.println("运行结束");

}

}

c28215ad4e700aaf7b3b79db78cb14df.png

从运行结果可知,run方法是在之后执行的,虽然start开启线程比  【System.out.println( "运行结束" );】 他早,这说明,CPU在调用线程的时候,是随机的

4,再次验证cpu调用线程的随机性

class MyThreadRand extendsThread{public voidrun(){try{for ( int i = 0; i < 10; i++) {int time = ( int )( Math.random() * 1000);

Thread.sleep( time );

System.out.println("MyThread:" +Thread.currentThread().getName() );

}

}catch( InterruptedException e ){

e.printStackTrace();

}

}

}public classRandThread {public static voidmain(String[] args) {try{

MyThreadRand mt= newMyThreadRand();

mt.setName("自定义线程");

mt.start();for ( int i = 0; i < 10; i++) {int time = ( int )( Math.random() * 1000);

Thread.sleep( time );

System.out.println("MainThread:" +Thread.currentThread().getName() );

}

}catch( InterruptedException e ){

e.printStackTrace();

}

}

}

12fb39fabef0a76e0dc067e078d8382c.png

从执行结果可知,线程的调度没有什么规律,是随机的, 这里补充一点,start方法作用是通知 “线程规划器”,这个线程已经准备好了,等待调用线程的run方法,就是让系统安排一个时间来调用run方法。如果直接调用run方法,线程就变成同步方式了,必须等待MyThreadRand的run方法执行完成之后,才会执行main函数中的线程

5,start方法的顺序,不代表线程的启动顺序

class MyThreadStart extendsThread{private inti;public MyThreadStart( inti ) {this.i =i;

}public voidrun(){

System.out.println( i );

}

}public classRandThread2 {public static voidmain(String[] args) {

MyThreadStart s1= new MyThreadStart( 1);

MyThreadStart s2= new MyThreadStart( 2);

MyThreadStart s3= new MyThreadStart( 3);

MyThreadStart s4= new MyThreadStart( 4);

MyThreadStart s5= new MyThreadStart( 5);

MyThreadStart s6= new MyThreadStart( 6);

MyThreadStart s7= new MyThreadStart( 7);

MyThreadStart s8= new MyThreadStart( 8);

MyThreadStart s9= new MyThreadStart( 9);

MyThreadStart s10= new MyThreadStart( 10);

s1.start();

s2.start();

s3.start();

s4.start();

s5.start();

s6.start();

s7.start();

s8.start();

s9.start();

s10.start();

}

}

04308aa9760e8b41ff73322b1b58f86f.png

6,实现Runnable接口

class MyThreadRunnable implementsRunnable {public voidrun(){

System.out.println( Thread.currentThread().getName() );

}

}public classThreadRunnable {public static voidmain(String[] args) {

MyThreadRunnable mt= newMyThreadRunnable();

Thread t= newThread( mt );

t.setName("自定义线程1");

t.start();

}

}

那么两种多线程的实现方式,有什么不同呢?

>继承Thread类

>实现Runnable接口

1,使用继承Thread类的方式,多线程之间的数据不共享

class MyThreadShare extendsThread{private int count = 5;publicMyThreadShare( String name ){this.setName( name );

}public voidrun(){while( count-- > 0){

System.out.println( Thread.currentThread().getName()+ "->" +count );

}

}

}public classThreadShare {public static voidmain(String[] args) {

MyThreadShare mt1= new MyThreadShare( "A");

MyThreadShare mt2= new MyThreadShare( "B");

MyThreadShare mt3= new MyThreadShare( "C");

mt1.start();

mt2.start();

mt3.start();

}

}

713cc77ea79e2f04e53ec4e836d0941e.png

2,而要想实现线程之间的数据共享,我们可以改一下

备注:线程数据共享与不共享,都有对应的场景,比如火车站4个窗口卖票,很显然需要线程共享数据。如:总共用10张票,如果窗口卖了1张,其他窗口就指剩下9张,这才是比较贴近实际的,如果用第一种方式,相当于有40张余票了。

class MyThreadShare2 extendsThread{private int count = 5;public voidrun(){while( count-- > 0){

System.out.println( Thread.currentThread().getName()+ "->" +count );

}

}

}public classThreadShare2 {public static voidmain(String[] args) {

MyThreadShare2 mt= newMyThreadShare2();

Thread ta= new Thread( mt, "A");

Thread tb= new Thread( mt, "B");

Thread tc= new Thread( mt, "C");

ta.start();

tb.start();

tc.start();

}

}

e88ad9d23d91d46e66b458efb83c98c5.png

从结果上看,好像实现了,数据共享,但是有点异常,B->3 很明显不对,这种现象,在多线程编程里面,叫“线程非安全”。现实生活中也有类似场景,比如4S店卖车,两个客户同时预订了这辆车。那估计少不了一番辩论。怎么解决这个问题呢?一般来说,在客户订车之前,销售员要先查看库存,如果客户下单,要把库存占用。表明有人预订,其他销售员看见了,就知道车被预订了。程序中也是类似。如果要访问这个变量,我们就给他加锁,类似于销售员占用库存。在方法前加上synchronized关键字。那么其他线程访问的时候,必须拿到这把锁,才能访问。synchronized可以在任意对象或者方法上加锁。

class MyThreadShare2 extendsThread{private int count = 5;//public void run(){//产生线程非安全问题

synchronized public voidrun(){while( count-- > 0){

System.out.println( Thread.currentThread().getName()+ "->" +count );

}

}

}public classThreadShare2 {public static voidmain(String[] args) {

MyThreadShare2 mt= newMyThreadShare2();

Thread ta= new Thread( mt, "A");

Thread tb= new Thread( mt, "B");

Thread tc= new Thread( mt, "C");

ta.start();

tb.start();

tc.start();

}

}

3,模拟用户登录场景,如果有两个用户登录,我们让其中一个用户线程占时挂起。看下会出现什么情况

classLogin {private staticString userName;private staticString userPwd;public static voiddoPost( String _userName, String _userPwd ){try{

userName=_userName;if( userName.equals( "ghostwu") ) {

Thread.sleep(3000);

}

userPwd=_userPwd;

System.out.println( userName+ "---->" +userPwd );

}catch( InterruptedException e ){

e.printStackTrace();

}

}

}class ThreadA extendsThread{public voidrun(){

Login.doPost("ghostwu", "abc123");

}

}class ThreadB extendsThread{public voidrun(){

Login.doPost("ghostwuB", "abc1234");

}

}public classUserLogin {public static voidmain(String[] args) {

ThreadA ta= newThreadA();

ThreadB tb= newThreadB();

ta.start();

tb.start();

}

}

5b821812dec9cdc9f80f25878f162518.png

在A线程挂起的时候,他之前的赋值已经被B线程改变了,所以结果与预想的ghostwu  abc123不同。很明显,我们要上锁。

synchronized public static voiddoPost( String _userName, String _userPwd ){try{

userName=_userName;if( userName.equals( "ghostwu") ) {

Thread.sleep(3000);

}

userPwd=_userPwd;

System.out.println( userName+ "---->" +userPwd );

}catch( InterruptedException e ){

e.printStackTrace();

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值