图文|Android 使用Thread 和多线程使用互斥锁

640?wx_fmt=jpeg

640?wx_fmt=jpeg

640?wx_fmt=png

为什么需要多线程进行开发?

多线程不管是嵌入式系统RTOS,Linux,还是应用开发,中间件开发,都是必不可少的,做一个技术的时候,如果能做到举一反三,下次使用的时候不会再遇到坑,我这次给出的例子是Android 的多线程开发。

640?wx_fmt=png

640?wx_fmt=png

如何使用一个线程

在Android 应用程序里面如何使用一个线程呢?直接看下面的代码,代码不是很多,如果需要用的话直接摘抄过去就好了。

//定义一个线程
private SendThread mSendThread = null;

/**
 * 线程实体
 */

private class SendThread extends Thread{

    public void run() {
    }
}

//实例化线程
if (mSendThread == null){
   mSendThread = new SendThread();
}

//启动线程
mSendThread.start();
640?wx_fmt=png

多次调用start是同一个堆栈空间吗?

如果只new了一次线程,多次start,会出现怎么样的情况呢?

android 线程start的函数原型如下

public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */

        // Android-changed: throw if 'started' is true
        if (threadStatus != 0 || started)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */

        group.add(this);

        started = false;
        try {
            nativeCreate(this, stackSize, daemon);
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */

            }
        }
        }

然后我做了下面的一个代码实验

/**
 * 发送线程实体
 */

private class SendThread extends Thread{
    int testCount = 20;

    public void run() {
        while(testCount >0)
        {
            Log.d(TAG,"testCount:"+testCount);
            testCount --;
        }
    }
}

//实例化线程
if (mSendThread == null){
   mSendThread = new SendThread();
}

//启动线程
mSendThread.start();
mSendThread.start();

结果输出如下

D/ttyusb: testCount:20
D/ttyusb: testCount:20
D/ttyusb: testCount:19
D/ttyusb: testCount:19
D/ttyusb: testCount:18
D/ttyusb: testCount:18
D/ttyusb: testCount:17
D/ttyusb: testCount:16
D/ttyusb: testCount:17
D/ttyusb: testCount:15
D/ttyusb: testCount:16
D/ttyusb: testCount:14
D/ttyusb: testCount:15
D/ttyusb: testCount:13
D/ttyusb: testCount:14
D/ttyusb: testCount:12
D/ttyusb: testCount:13
D/ttyusb: testCount:11
D/ttyusb: testCount:12
D/ttyusb: testCount:10
D/ttyusb: testCount:9
D/ttyusb: testCount:11
D/ttyusb: testCount:8
D/ttyusb: testCount:10
D/ttyusb: testCount:7
D/ttyusb: testCount:9
D/ttyusb: testCount:6
D/ttyusb: testCount:5
D/ttyusb: testCount:8
D/ttyusb: testCount:4
D/ttyusb: testCount:7
D/ttyusb: testCount:3
D/ttyusb: testCount:6
D/ttyusb: testCount:2
D/ttyusb: testCount:1
D/ttyusb: testCount:5
D/ttyusb: testCount:4
D/ttyusb: testCount:3
D/ttyusb: testCount:2
D/ttyusb: testCount:1

可以看出线程每次start后,他们使用的堆栈空间是不相同的。

640?wx_fmt=png

在双线程里面使用互斥锁

使用互斥锁的情况非常普遍,但是新手写代码肯定会有道意想不到的问题,我就是那个新手,我就遇到了那个意想不到的问题。

640?wx_fmt=png

给出下面一段代码

/**
     * 线程实体
     */

    public class SendThread extends Thread {


        public void run() {
            isStart = true;
            for(int i=0;i<20;i++)
            {
                lock.lock();
                successCount = i;
                lock.unlock();
                Log.d(TAG, "Write:testCount:" + successCount);
            }
            isStart = false;
        }
    }

    /**
     * 接收数据的线程
     */

    public class ReceiveThread extends Thread {

        @Override
        public void run() {

            int testCount = 20;

            super.run();
            //条件判断,只要条件为true,则一直执行这个线程
            while (isStart == true) {

                testCount = successCount;

                Log.d(TAG, "Read:testCount:" + testCount);
            }

        }
    }

代码执行的流程大概如下

640?wx_fmt=png
640?wx_fmt=png

代码输出


03-08 11:41:35.383 14866-14906/? D/TEST: Write:testCount:0
03-08 11:41:35.383 14866-14866/? D/TEST: 启动线程完成
03-08 11:41:35.383 14866-14906/? D/TEST: Write:testCount:1
03-08 11:41:35.383 14866-14906/? D/TEST: Write:testCount:2
03-08 11:41:35.383 14866-14907/? D/TEST: Read:testCount:1
03-08 11:41:35.383 14866-14906/? D/TEST: Write:testCount:3
03-08 11:41:35.383 14866-14907/? D/TEST: Read:testCount:3
03-08 11:41:35.383 14866-14906/? D/TEST: Write:testCount:4
03-08 11:41:35.383 14866-14907/? D/TEST: Read:testCount:4
03-08 11:41:35.383 14866-14906/? D/TEST: Write:testCount:5
03-08 11:41:35.383 14866-14907/? D/TEST: Read:testCount:5
03-08 11:41:35.383 14866-14906/? D/TEST: Write:testCount:6
03-08 11:41:35.383 14866-14907/? D/TEST: Read:testCount:6
03-08 11:41:35.383 14866-14906/? D/TEST: Write:testCount:7
03-08 11:41:35.383 14866-14907/? D/TEST: Read:testCount:7
03-08 11:41:35.383 14866-14906/? D/TEST: Write:testCount:8
03-08 11:41:35.383 14866-14907/? D/TEST: Read:testCount:8
03-08 11:41:35.383 14866-14906/? D/TEST: Write:testCount:9
03-08 11:41:35.383 14866-14907/? D/TEST: Read:testCount:9
03-08 11:41:35.383 14866-14906/? D/TEST: Write:testCount:10
03-08 11:41:35.383 14866-14907/? D/TEST: Read:testCount:10
03-08 11:41:35.383 14866-14906/? D/TEST: Write:testCount:11
03-08 11:41:35.383 14866-14907/? D/TEST: Read:testCount:11
03-08 11:41:35.383 14866-14906/? D/TEST: Write:testCount:12
03-08 11:41:35.383 14866-14907/? D/TEST: Read:testCount:12
03-08 11:41:35.383 14866-14906/? D/TEST: Write:testCount:13
03-08 11:41:35.383 14866-14907/? D/TEST: Read:testCount:13
03-08 11:41:35.383 14866-14906/? D/TEST: Write:testCount:14
03-08 11:41:35.383 14866-14907/? D/TEST: Read:testCount:14
03-08 11:41:35.383 14866-14906/? D/TEST: Write:testCount:15
03-08 11:41:35.383 14866-14907/? D/TEST: Read:testCount:15
03-08 11:41:35.383 14866-14906/? D/TEST: Write:testCount:16
03-08 11:41:35.383 14866-14907/? D/TEST: Read:testCount:16
03-08 11:41:35.383 14866-14906/? D/TEST: Write:testCount:17
03-08 11:41:35.383 14866-14907/? D/TEST: Read:testCount:17
03-08 11:41:35.383 14866-14906/? D/TEST: Write:testCount:18
03-08 11:41:35.383 14866-14907/? D/TEST: Read:testCount:18
03-08 11:41:35.383 14866-14906/? D/TEST: Write:testCount:19
03-08 11:41:35.383 14866-14907/? D/TEST: Read:testCount:19

上述代码问题

我们希望的代码流程是,我发送一个数据,你就接收一个数据,不能出现数据丢失的情况,但是上面的日子来看,接收数据是发生了丢失,这样的情况如果在实际应用中是非常危险的。

640?wx_fmt=png

所以代码需要类似下面这样修改

    /**
     * 线程实体
     */

    public class SendThread extends Thread {


        public void run() {
            isStart = true;
            for(int i=0;i<20;i++)
            {
                Log.d(TAG,"开始写线程~");
                lock.lock();
                successCount = i;
                Log.d(TAG, "写数据:testCount:" + successCount);
                lock.unlock();
                Log.d(TAG,"写线程休眠~");
                sendTreadSleep(10);
            }
            isStart = false;
        }
    }

    /**
     * 接收数据的线程
     */

    public class ReceiveThread extends Thread {

        @Override
        public void run() 
{

            int testCount = 20;

            super.run();
            //条件判断,只要条件为true,则一直执行这个线程
            while (isStart == true) {
                Log.d(TAG,"开始读线程~");
                lock.lock();
                testCount = successCount;
                Log.d(TAG, "读数据~:testCount:" + testCount);
                lock.unlock();
                Log.d(TAG,"读线程休眠~");
                receiveTreadSleep(5);
            }

        }
    }

工程的Demo到时候在文末给出,写线程在写完后,就休眠10MS,然后再到读线程执行,加的日志非常方便大家观看两个线程之间的运行逻辑。

640?wx_fmt=png

日志输出如下

 D/TEST: 启动线程完成
 D/TEST: 开始写线程~
 D/TEST: 写数据:testCount:0
 D/TEST: 写线程休眠~
 D/TEST: 开始读线程~
 D/TEST: 读数据~:testCount:0
 D/TEST: 读线程休眠~
 D/TEST: 开始读线程~
 D/TEST: 读数据~:testCount:0
 D/TEST: 读线程休眠~
 D/TEST: 开始写线程~
 D/TEST: 写数据:testCount:1
 D/TEST: 写线程休眠~
 D/TEST: 开始读线程~
 D/TEST: 读数据~:testCount:1
 D/TEST: 读线程休眠~
 D/TEST: 开始读线程~
 D/TEST: 读数据~:testCount:1
 D/TEST: 读线程休眠~
 D/TEST: 开始写线程~
 D/TEST: 写数据:testCount:2
 D/TEST: 写线程休眠~
 D/TEST: 开始读线程~
 D/TEST: 读数据~:testCount:2
 D/TEST: 读线程休眠~
 D/TEST: 开始读线程~
 D/TEST: 读数据~:testCount:2
 D/TEST: 读线程休眠~
 D/TEST: 开始写线程~
 D/TEST: 写数据:testCount:3
 D/TEST: 写线程休眠~
 D/TEST: 开始读线程~
 D/TEST: 读数据~:testCount:3
 D/TEST: 读线程休眠~
 D/TEST: 开始写线程~
 D/TEST: 开始读线程~
 D/TEST: 写数据:testCount:4
 D/TEST: 写线程休眠~
 D/TEST: 读数据~:testCount:4
 D/TEST: 读线程休眠~
 D/TEST: 开始读线程~
 D/TEST: 读数据~:testCount:4
 D/TEST: 读线程休眠~
 D/TEST: 开始写线程~
 D/TEST: 写数据:testCount:5
 D/TEST: 写线程休眠~
 D/TEST: 开始读线程~
 D/TEST: 读数据~:testCount:5
 D/TEST: 读线程休眠~
 D/TEST: 开始读线程~
 D/TEST: 读数据~:testCount:5
 D/TEST: 读线程休眠~
 D/TEST: 开始写线程~
 D/TEST: 写数据:testCount:6
 D/TEST: 写线程休眠~
 D/TEST: 开始读线程~
 D/TEST: 读数据~:testCount:6
 D/TEST: 读线程休眠~
 D/TEST: 开始读线程~
 D/TEST: 读数据~:testCount:6
 D/TEST: 读线程休眠~
 D/TEST: 开始写线程~
 D/TEST: 写数据:testCount:7
 D/TEST: 写线程休眠~
 D/TEST: 开始读线程~
 D/TEST: 读数据~:testCount:7
 D/TEST: 读线程休眠~
 D/TEST: 开始读线程~
 D/TEST: 读数据~:testCount:7
 D/TEST: 读线程休眠~
 D/TEST: 开始写线程~
 D/TEST: 写数据:testCount:8
 D/TEST: 写线程休眠~
 D/TEST: 开始读线程~
 D/TEST: 读数据~:testCount:8
 D/TEST: 读线程休眠~
 D/TEST: 开始读线程~
 D/TEST: 读数据~:testCount:8
 D/TEST: 读线程休眠~
 D/TEST: 开始写线程~
 D/TEST: 写数据:testCount:9
 D/TEST: 写线程休眠~
 D/TEST: 开始读线程~
 D/TEST: 读数据~:testCount:9
 D/TEST: 读线程休眠~
 D/TEST: 开始读线程~
 D/TEST: 读数据~:testCount:9
 D/TEST: 读线程休眠~
 D/TEST: 开始写线程~
 D/TEST: 写数据:testCount:10
 D/TEST: 写线程休眠~
 D/TEST: 开始读线程~
 D/TEST: 读数据~:testCount:10
 D/TEST: 读线程休眠~
grande.ttyusb.test0001/com.evergrande.ttyusb.test0001.MainActivity] connect: already connected (cur=1 req=1)
 D/TEST: 开始读线程~
 D/TEST: 开始写线程~
 D/TEST: 读数据~:testCount:10
 D/TEST: 读线程休眠~
 D/TEST: 写数据:testCount:11
 D/TEST: 写线程休眠~
 D/mali_winsys: EGLint new_window_surface(egl_winsys_display*, void*, EGLSurface, EGLConfig, egl_winsys_surface**, egl_color_buffer_format*, EGLBoolean) returns 0x3000
 D/TEST: 开始读线程~
 D/TEST: 读数据~:testCount:11
 D/TEST: 读线程休眠~
 D/TEST: 开始写线程~
 D/TEST: 写数据:testCount:12
 D/TEST: 写线程休眠~
 D/TEST: 开始读线程~
 D/TEST: 读数据~:testCount:12

使用synchronized来达到完成互斥

synchronized修饰的方法可以让两个方法之间完成互斥,比如写和读互斥,写和写互斥,读和读互斥,都可以用这个方法。
使用代码如下

  /**
     * 线程实体
     */

    public class SendThread extends Thread {


        public void run() {
            isStart = true;
            for(int i=0;i<10;i++)
            {
                Log.d(TAG,"开始写线程~");
                mDataValue.setData(i);
                Log.d(TAG, "写数据:testCount:" + successCount);
                Log.d(TAG,"写线程休眠~");
                sendTreadSleep(10);
            }
            isStart = false;
        }
    }

    /**
     * 接收数据的线程
     */

    public class ReceiveThread extends Thread {

        @Override
        public void run() {

            int testCount = 20;
            Log.d(TAG,"s开始读线程~");
            super.run();
            //条件判断,只要条件为true,则一直执行这个线程
            while (isStart == true) {
                Log.d(TAG,"开始读线程~");
                testCount = mDataValue.getData();
                Log.d(TAG, "读数据~:testCount:" + testCount);
                Log.d(TAG,"读线程休眠~");
                receiveTreadSleep(5);
            }
        }
    }

    /**
     * 发送线程延迟
     * @param millis 毫秒
     */

    private void sendTreadSleep(int millis)
    
{
        try{
            mSendThread.sleep(millis);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 接收线程延迟
     * @param millis 毫秒
     */

    private void receiveTreadSleep(int millis)
    
{
        try{
            mReceiveThread.sleep(millis);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private class DataValue{

        private synchronized void setData(int value){
            Log.d(TAG,"设置数据~setData");
            successCount = value;
        }

        private synchronized  int getData(){
            Log.d(TAG,"获取数据~getData");
            return successCount;
        }

    }

读写锁ReentrantReadWriteLock

上面是使用互斥锁,这里介绍一个读写锁,也是用来完成互斥的。使用代码如下

    /**
     * 线程实体
     */

    public class SendThread extends Thread {


        public void run() {
            isStart = true;
            for(int i=0;i<10;i++)
            {
                mrwDataValue.setData(i);
                Log.d(TAG,Thread.currentThread().getName() +"写休眠");
                sendTreadSleep(10);
            }
            isStart = false;
        }
    }

    /**
     * 接收数据的线程
     */

    public class ReceiveThread extends Thread {

        @Override
        public void run() 
{

            int testCount = 20;
            Log.d(TAG,"s开始读线程~");
            super.run();
            //条件判断,只要条件为true,则一直执行这个线程
            while (isStart == true) {
                mrwDataValue.getData();
                Log.d(TAG,Thread.currentThread().getName() +"读休眠");
                receiveTreadSleep(5);
            }
        }
    }
    /*
     * 使用读写锁完成互斥ReadWriteLock
     */

    private class rwDataValue{
        private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
        private int Data;

        private void setData(int value){
            readWriteLock.writeLock().lock();
            try {
                Log.d(TAG, Thread.currentThread().getName() +"写数据~setData:"+value);
                Data = value;
                Thread.sleep(30);
            }catch (Exception i){
                Log.e(TAG,"error");
            }finally {
                readWriteLock.writeLock().unlock();
            }
        }

        private  void getData(){
            readWriteLock.readLock().lock();
            try {
                Log.d(TAG,Thread.currentThread().getName() +"获取数据~getData:" +Data);
                Thread.sleep(10);
            }catch (Exception i){
                Log.e(TAG,"error");
            }finally {
                readWriteLock.readLock().unlock();
            }
        }
640?wx_fmt=png

640?wx_fmt=png

640?wx_fmt=png

640?wx_fmt=png

参考

https://blog.csdn.net/zy_style/article/details/53423877

Demo 代码

https://github.com/weiqifa0/androitdThread


640?wx_fmt=png


当你看到这里的时候,说明你已经阅读完上面的内容

不管怎样,感谢您有心或者无意的关注和支持

想获取学习1024G资料,请点击状态栏公众号福利按钮


640?wx_fmt=jpeg




本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/468748.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

01.轮播图之三 : collectionView 轮播

个人觉得 collection view 做轮播是最方便的&#xff0c;设置下flowlayout 其他不会有很大的变动&#xff0c;没有什么逻辑的代码 lets begin…… 创建自定义的view .h 声明文件 interface CollectionViewShuffling : UIViewproperty (nonatomic,strong)NSArray *array;end .m …

JDBC入门级操作

JDBC:数据库连接, 通过接口实现不同数据库之间的切换, 面向接口编程 JDBC驱动程序将JDBC调用映射成特定的数据库调用 驱动类型: 1.JDBC驱动:JDBC-ODBC桥,最早的实现方式,将JDBC API映射到ODBC API,Java8中已删除 2.直接将JDBC API映射成数据库特定的客户端API,次驱动包含特定数…

手机调试python的软件_Appium+Python(ios真机移动端App H5混合自动化实战测试)

一、环境搭建篇(一)安装JDK大家自行安装(二)安装Appium1、appium desktop2、appium server> brew install node # get node.js> npm install -g appium # get appium> npm install wd # get appium client> appium & # start appium> node your-appium-test…

计算机专业的学生该选择日后的人生道路?继续从事IT还是考公务员……

问题来自知乎原问题如下&#xff1a;知乎上的盆友们大家好&#xff01;我是某高校大二学生。万能的知友们&#xff0c;有相关经验的大家给点宝贵意见呗&#xff0c;万分感谢。个人问题&#xff1a;我当时是听说这一行工资高&#xff0c;因为家里条件一般&#xff0c;所以想要挣…

file_put_contents()写入数组

file_put_contents()写入数组 $arr array(name>张三&#xff0c;‘age’>26, sex>男 ) $str var_export($arr,TRUE); file_put_contents($filename,$str);转载于:https://www.cnblogs.com/icefei/p/6773916.html

JVM垃圾收集器与内存分配策略学习总结

方法区: 1.线程共享 2.储存类信息,常量,静态变量,编译器编译后的代码 3.非堆(别名)用于区分Java堆 4.不需要连续的内存 5.可以固定或可扩张 6.选择不实现垃圾回收//这个区域很少进行垃圾回收 7.针对常量池回收 8.对类型的卸载 9.无法满足内存分配需求抛出OutOfMemoryError4 虚…

汇编比较两个数大小_计算机是怎样跑起来的 -- 体验一次汇编过程

标志内存或I/O中存储单元的数字叫做“地址”。CPU中的标志寄存器有什么作用&#xff1f;用于在运算指令执行后&#xff0c;存储运算结果的某些状态。从程序员的角度看硬件CPU&#xff1a;种类&#xff0c;时钟信号的频率。可以使用哪种机器语言取决于CPU的种类。内存信息&#…

Nginx 作为 WebSockets 代理

WebSocket 协议给我们提供了一个创建可以支持客户端和服务端进行双向实时通信的web应用程序的方法。相比之前使用的方法&#xff0c;WebSocket&#xff08;作为HTML5的一部分&#xff09;可以使我们更容易开的发出这种类型的应用程序。绝大多数的现代浏览器都支持WebSocket&…

设计模式小结

开始的毛病:变量命名不规范,if-else判断有的代码做无用功,代码健壮性太差,没有做try-cath异常处理 工厂模式(创建型模式): 创建对象接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建延伸到子类进行 主要解决接口选择问题,明确计划不同条件下执行创建不同实例 通过子…

android区块链 钱包_区块链钱包Cashbox 开发工程师聊一聊开源

今天与大家聊一聊软件开源。事情的起因是一次闲谈的时候&#xff0c;市场部的同事表示&#xff1a;不做软件开发的人不能够理解为什么要把软件源代码公开出去。当时的我很惊讶&#xff0c;因为就像他们不理解为什么要开源一样&#xff0c;我也不理解他们为什么不理解为什么要开…

dom4j创建、解析xml文件(增删改查)

先对xml文件进行解析,xml文件如下图所示 <?xml version"1.0" encoding"UTF-8"?><?eclipse version"3.4"?> <student1> <studentID>001</studentID> <name>sanny</name> <major>…

Java的Class类文件结构及基本字节码指令

Class类文件的结构 概念:Class文件是一组以8位字节为基础单位的二进制流 按顺序整齐排列 没有任何分隔符,内容全部是运行时的必要数据,没有空隙存在 排序方式:高位在前 Big-Endian:最高字节在地址最低位,最低字节在地址最高位 Little-Endian:相反 储存方式:类似于C语言结构体…

Android 7.1 32位apk导致的系统库找不到问题

出现问题 因为我们安卓7.1系统的应用都是64位的APK&#xff0c;我们自己开发的应用是32位的APK&#xff0c;这样在启动的时候&#xff0c;就大概率的出现库找不到的问题。 错误的日志如下&#xff1a; Line 440: 03-12 10:48:39.620 634 634 W PackageManager: Instruct…

python基础知识下载_Python基础知识(一)

一、安装pythonwindow系统下先下载的 python 安装包&#xff0c;直接安装即可。为确认是否安装正确&#xff0c;可用系统记事本创建一个hello.py的程序其中包含如下内容&#xff1a;1 #!/usr/bin/env python32 print("Hello", "World!")代码说明&#xff1…

单词的理解 —— 词义的变化(翻译)

mimic&#xff1a;模仿&#xff0c;摹拟&#xff08;而不是最小化&#xff09;minder&#xff1a;n. 看守者&#xff1b;照顾者 保镖等近似含义&#xff1b;outstanding&#xff0c;outstanding work&#xff1a; 未完成的工作&#xff0c;backlog&#xff1a;n. [管理] 积压的…

JVM类加载机制_字节码执行引擎_Java内存模型

类加载机制: 类加载生命期:加载(Loading),验证(Verification),准备(Preparation),解析(Resolution),初始化(Initialization),使用(Using),卸载(Unloading)初始化:1.遇到new,getstatic,putstatic,invokestatic指令,类没有进行初始化,先触发初始化2.java反射机制3.初始化一个类,…

最新车载导航端口检测工具_高德地图这个功能 把微信都没做好的车载社交解决了?...

在汽车网联化和智能化带给人们的诸多想象里&#xff0c;车内社交似乎是最虚无缥缈的那个。在移动互联网领域&#xff0c;微信通过满足人们社交需求这个基本点&#xff0c;构建起庞大的用户群体&#xff0c;展示出巨大的市场前景。但在驾车场景下&#xff0c;车载社交该怎么玩&a…

王译潇20162314 第九周作业总结

学号 20162314 2016-2017-2 《Java程序设计》第九周学习总结 教材学习内容总结 数据库是为其他程序提供数据的应用软件 关系数据库通过唯一的标识符在不同表的记录间建立了关系 JDBC API用来建立到数据库的连接 CREATE TABLE SQL 语句用来创建新的数据库表 ALTER TABLE SQL 语句…

sketchup边线设置_春天花花天桥,SketchUp草图大师快速建模!

最近小吧在网上看到一座设计很特别的天桥&#xff0c;形状很像一朵花&#xff0c;名字也十分写实——春花天桥。喏&#xff0c;就是下面这个家伙&#xff01;图片来自网络春花天桥是2011年深圳举办大运会前夕投资建设的形象提升工程之一&#xff0c;在建设初期就被定位为地标性…

自言自语

随笔, 自我审视, 需要反思 最近这段时间, 各种艰难困苦早已将最初的激情冲刷地只剩下骨头了, 每天拖着疲惫的身子, 总是做着没有意义的事; 迷失了自我, 总把时间浪费, 而在做浪费时间的事却没有思考做这件事是否是有意义的, 是否是有价值的, 是否是在浪费时间; 没有思考, 就像行…